home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / system-config-printer / system-config-printer-kde.py < prev    next >
Text File  |  2008-08-12  |  129KB  |  3,207 lines

  1. #!/usr/bin/env python
  2.  
  3. #############################################################################
  4. ##
  5. ## Copyright (C) 2007 Canonical Ltd
  6. ## Author: Jonathan Riddell <jriddell@ubuntu.com>
  7. ##
  8. ## Includes code from System Config Printer
  9. ## Copyright (C) 2007 Tim Waugh <twaugh@redhat.com>
  10. ## Copyright (C) 2007 Red Hat, Inc.
  11. ##
  12. ## This program is free software; you can redistribute it and/or
  13. ## modify it under the terms of the GNU General Public License as
  14. ## published by the Free Software Foundation; either version 2 of
  15. ## the License, or (at your option) any later version.
  16. ##
  17. ## This program is distributed in the hope that it will be useful,
  18. ## but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20. ## GNU General Public License for more details.
  21. ##
  22. ## You should have received a copy of the GNU General Public License
  23. ## along with this program.  If not, see <http://www.gnu.org/licenses/>.
  24. ##
  25. #############################################################################
  26.  
  27. MIN_REFRESH_INTERVAL = 1 # seconds
  28. domain="system-config-printer"
  29. import locale
  30.  
  31. pkgdata = '/usr/share/' + domain
  32.  
  33. import sys, os, time, traceback, re, tempfile, httplib
  34. #tempfile
  35. import thread
  36.  
  37. if os.path.exists("system-config-printer.ui"):
  38.     APPDIR="."
  39. else:
  40.     APPDIR="/usr/share/system-config-printer"
  41.  
  42. #load modules from system-config-printer-common (debug, smburi), change path here if you have it installed elsewhere
  43. SYSTEM_CONFIG_PRINTER_DIR = "/usr/share/system-config-printer"
  44. if os.path.exists(SYSTEM_CONFIG_PRINTER_DIR + "/debug.py"):
  45.     sys.path.append(SYSTEM_CONFIG_PRINTER_DIR)
  46.  
  47. from PyQt4.QtCore import *
  48. from PyQt4.QtGui import *
  49. from PyQt4 import uic
  50.  
  51. import gettext
  52. gettext.textdomain(domain)
  53. def _(string):
  54.     return unicode(gettext.gettext(string), "utf-8")
  55.  
  56. def translate(self, prop):
  57.     """reimplement method from uic to change it to use gettext"""
  58.     if prop.get("notr", None) == "true":
  59.         return self._cstring(prop)
  60.     else:
  61.         if prop.text is None:
  62.             return ""
  63.         text = prop.text.encode("UTF-8")
  64.         return _(text)
  65.  
  66. uic.properties.Properties._string = translate
  67.  
  68. import cups
  69. cups.require ("1.9.27")
  70.  
  71. # These come from system-config-printer
  72. import config
  73. import cupshelpers  #, options
  74. from smburi import SMBURI
  75. from debug import *
  76.  
  77. import dbus
  78. import dbus.mainloop.qt
  79. import dbus.service
  80.  
  81. ellipsis = unichr(0x2026)
  82.  
  83. try:
  84.     try_CUPS_SERVER_REMOTE_ANY = cups.CUPS_SERVER_REMOTE_ANY
  85. except AttributeError:
  86.     # cups module was compiled with CUPS < 1.3
  87.     try_CUPS_SERVER_REMOTE_ANY = "_remote_any"
  88.  
  89. def validDeviceURI (uri):
  90.     """Returns True is the provided URI is valid."""
  91.     if uri.find (":/") > 0:
  92.         return True
  93.     return False
  94.  
  95. class GUI(QWidget):
  96.     """our main class is the main window"""
  97.  
  98.     printer_states = { cups.IPP_PRINTER_IDLE: _("Idle"),
  99.                        cups.IPP_PRINTER_PROCESSING: _("Processing"),
  100.                        cups.IPP_PRINTER_BUSY: _("Busy"),
  101.                        cups.IPP_PRINTER_STOPPED: _("Stopped") }
  102.  
  103.     def __init__(self, start_printer = None, change_ppd = False):
  104.         QWidget.__init__(self)
  105.  
  106.         try:
  107.             self.language = locale.getlocale(locale.LC_MESSAGES)
  108.             self.encoding = locale.getlocale(locale.LC_CTYPE)
  109.         except:
  110.             nonfatalException()
  111.             os.environ['LC_ALL'] = 'C'
  112.             locale.setlocale (locale.LC_ALL, "")
  113.             self.language = locale.getlocale(locale.LC_MESSAGES)
  114.             self.encoding = locale.getlocale(locale.LC_CTYPE)
  115.  
  116.         self.printer = None
  117.         self.conflicts = set() # of options
  118.         self.connect_server = (self.printer and self.printer.getServer()) \
  119.                                or cups.getServer()    
  120.         self.connect_user = cups.getUser()
  121.         self.password = '' #FIXME not in Gnome version
  122.         self.passwd_retry = False #FIXME not in Gnome version
  123.         self.widget_data_setting = {} #FIXME not in Gnome version
  124.         #FIXMEcups.setPasswordCB(self.cupsPasswdCallback)        
  125.         ##self.server_is_publishing = False #FIXME new in Gnome version
  126.  
  127.         self.changed = set() # of options
  128.  
  129.         self.servers = set((self.connect_server,))
  130.  
  131.         try:
  132.             self.cups = cups.Connection()
  133.         except RuntimeError:
  134.             self.cups = None
  135.  
  136.         """
  137.         self.mainWindow = QWidget()
  138.         uic.loadUi(APPDIR + "/" + "system-config-printer.ui", self.mainWindow)
  139.         self.mainWindow.show()
  140.         """
  141.         #self.mainWindow = QWidget()
  142.         uic.loadUi(APPDIR + "/" + "system-config-printer.ui", self)
  143.         self.show()
  144.  
  145.         # New Printer Dialog
  146.         self.newPrinterGUI = np = NewPrinterGUI(self)
  147.         #np.NewPrinterWindow.set_transient_for(self.MainWindow)
  148.  
  149.         self.setConnected()
  150.  
  151.         self.connect(self.mainlist, SIGNAL("itemSelectionChanged()"), self.on_tvMainList_cursor_changed)
  152.         self.connect(self.mainlist, SIGNAL("currentItemChanged (QTreeWidgetItem*, QTreeWidgetItem*)"), self.on_tvMainList_changed)
  153.         self.connect(self.chkServerBrowse, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  154.         self.connect(self.chkServerShare, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  155.         self.connect(self.chkServerShareAny, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  156.         self.connect(self.chkServerRemoteAdmin, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  157.         self.connect(self.chkServerAllowCancelAll, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  158.         self.connect(self.chkServerLogDebug, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  159.  
  160.         self.connect(self.btnNewClass, SIGNAL("clicked()"), self.on_new_class_activate)
  161.         self.connect(self.btnNewPrinter, SIGNAL("clicked()"), self.on_new_printer_activate)
  162.  
  163.         self.connect(self.entPDescription, SIGNAL("textEdited(const QString&)"), self.on_printer_changed)
  164.         self.connect(self.entPLocation, SIGNAL("textEdited(const QString&)"), self.on_printer_changed)
  165.         self.connect(self.entPDevice, SIGNAL("textEdited(const QString&)"), self.on_printer_changed)
  166.         self.connect(self.chkPEnabled, SIGNAL("stateChanged(int)"), self.on_printer_changed)
  167.         self.connect(self.chkPAccepting, SIGNAL("stateChanged(int)"), self.on_printer_changed)
  168.         self.connect(self.chkPShared, SIGNAL("stateChanged(int)"), self.on_printer_changed)
  169.         self.connect(self.cmbPErrorPolicy, SIGNAL("currentIndexChanged(int)"), self.on_printer_changed)
  170.         self.connect(self.cmbPOperationPolicy, SIGNAL("currentIndexChanged(int)"), self.on_printer_changed)
  171.         self.connect(self.cmbPStartBanner, SIGNAL("currentIndexChanged(int)"), self.on_printer_changed)
  172.         self.connect(self.cmbPEndBanner, SIGNAL("currentIndexChanged(int)"), self.on_printer_changed)
  173.         #self.connect(self.rbtnPAllow, SIGNAL("toggled(bool)"), self.on_printer_changed)
  174.  
  175.         try:
  176.             self.populateList(start_printer, change_ppd)
  177.         except cups.HTTPError, (s,):
  178.             self.cups = None
  179.             self.setConnected()
  180.             self.populateList()
  181.             self.show_HTTP_Error(s)
  182.  
  183.         self.mainlist.header().hide()
  184.  
  185.         #hide some bits until implemented
  186.         self.btnNewPrinterNetwork.hide()
  187.         self.newPrinterNetworkLabel.hide()
  188.         self.btnNewPrinterSpecial.hide()
  189.         self.newPrinterSpecialLabel.hide()
  190.         self.btnNewPrinter.setText(_("New Printer"))
  191.         self.btnPrinterPropertiesApply.setIcon(QIcon(APPDIR + "/dialog-ok-apply.png"))
  192.         self.btnRevert.setIcon(QIcon(APPDIR + "/document-revert.png"))
  193.         self.newPrinterLabel.hide()
  194.         #(obsolete) only show settings until ready for the rest
  195.         #self.mainlist.hide()
  196.         self.mainlist.setCurrentItem(self.settingsItem)
  197.         #FIXME hide labels until implemented
  198.         self.lblPOptions.hide()
  199.         self.lblPInstallOptions.hide()
  200.  
  201.         self.setWindowIcon(QIcon(APPDIR + "/printer-128.png"))
  202.  
  203.     # now called  dests_iconview_item_activated() in the Gnome version
  204.     def on_tvMainList_cursor_changed(self):
  205.         if self.changed:
  206.             # The unapplied changes for this item have not been saved,
  207.             # and the user just pressed "Cancel".
  208.             #FIXME, should offer dialog prompting to save or cancel here
  209.             return
  210.         items = self.mainlist.selectedItems()
  211.         if len(items) < 1:
  212.             return
  213.         item = items[0]
  214.         #FIXME only show settings until ready for the rest
  215.         #item = self.settingsItem
  216.         type = str(item.text(1))
  217.         name = str(item.text(0))
  218.         #name, type = self.getSelectedItem()
  219.         #model, self.mainListSelected = self.tvMainList.get_selection().get_selected()
  220.         #Save the values incase it gets deselected
  221.         self.mainListSelectedType = type
  222.         self.mainListSelectedName = name
  223.         item_selected = True
  224.         if type == "New":
  225.             #self.ntbkMain.set_current_page(0)
  226.             self.ntbkMain.setCurrentIndex(0)
  227.         elif type == "Settings":
  228.             #self.ntbkMain.set_current_page(0)
  229.             self.ntbkMain.setCurrentIndex(1)
  230.             if self.cups:
  231.                 self.fillServerTab()
  232.             else:
  233.                 # No connection to CUPS.  Make sure the Apply/Revert buttons
  234.                 # are not sensitive.
  235.                 self.setDataButtonState()
  236.             item_selected = False
  237.         elif type in ['Printer', 'Class']:
  238.             try:
  239.                 self.fillPrinterTab(name)
  240.                 self.fillPrinterOptions()
  241.                 self.setDataButtonState()
  242.             except RuntimeError:
  243.                 # Perhaps cupsGetPPD2 failed for a browsed printer.
  244.                 self.ntbkMain.setCurrentIndex(3)
  245.                 #self.ntbkMain.set_current_page(2)
  246.                 return
  247.  
  248.             #self.ntbkMain.set_current_page(1)
  249.             self.ntbkMain.setCurrentIndex(2)
  250.         elif type == "None":
  251.             #self.ntbkMain.set_current_page(2)
  252.             self.ntbkMain.setCurrentIndex(3)
  253.             self.setDataButtonState()
  254.             item_selected = False
  255.  
  256.         """FIXME, copy button
  257.         is_local = item_selected and not self.printers[name].discovered
  258.         for widget in [self.copy, self.btnCopy]:
  259.             widget.set_sensitive(item_selected)
  260.         for widget in [self.delete, self.btnDelete]:
  261.             widget.set_sensitive(is_local)
  262.         """
  263.  
  264.     def printer_properties_response(self):
  265.         name, type = self.getSelectedItem()
  266.         if type in ("Printer", "Class"):
  267.             return self.save_printer(self.printer)
  268.         elif type == "Settings":
  269.             return self.save_serversettings()
  270.  
  271.     def on_tvMainList_changed(self, new, old):
  272.         """about to change, offer to save"""
  273.         if self.changed:
  274.             answer = QMessageBox.question(self, "Save Changes", "Do you want to save changes?", QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save)
  275.             if answer == QMessageBox.Save:
  276.                 self.printer_properties_response()
  277.             elif answer == QMessageBox.Discard:
  278.                 self.changed = set() # avoid asking the user
  279.  
  280.     def busy (self, win = None):
  281.         try:
  282.             if not win:
  283.                 win = self
  284.             win.setCursor(Qt.WaitCursor)
  285.             QApplication.processEvents()
  286.         except:
  287.             nonfatalException ()
  288.  
  289.     def ready (self, win = None):
  290.         try:
  291.             if not win:
  292.                 win = self
  293.             win.setCursor(Qt.ArrowCursor)
  294.             QApplication.processEvents()
  295.         except:
  296.             nonfatalException ()
  297.  
  298.     def setConnected(self):
  299.         connected = bool(self.cups)
  300.  
  301.         host = cups.getServer()
  302.  
  303.         if host[0] == '/':
  304.             host = 'localhost'
  305.         self.setWindowTitle(_("Printer configuration - %s") % host)
  306.  
  307.         if connected:
  308.             status_msg = _("Connected to %s") % host
  309.         else:
  310.             status_msg = _("Not connected")
  311.         #FIXME do we want a statusbar? 
  312.         #self.statusbarMain.push(self.status_context_id, status_msg)
  313.  
  314.         for widget in (#FIXMEself.btnNewPrinter, self.btnNewClass,
  315.                        #self.new_printer, self.new_class,
  316.                        self.chkServerBrowse, self.chkServerShare,
  317.                        self.chkServerRemoteAdmin,
  318.                        self.chkServerAllowCancelAll,
  319.                        self.chkServerLogDebug):
  320.             widget.setEnabled(connected)
  321.  
  322.         sharing = self.chkServerShare.isChecked ()
  323.         self.chkServerShareAny.setEnabled (sharing)
  324.  
  325.         try:
  326.             del self.server_settings
  327.         except:
  328.             pass
  329.  
  330.     def populateList(self, start_printer = None, change_ppd = False):
  331.         #FIXMEold_name, old_type = self.getSelectedItem()
  332.         old_name = ""
  333.         old_type = ""
  334.  
  335.         select_path = None
  336.  
  337.         if self.cups:
  338.             try:
  339.                 # get Printers
  340.                 self.printers = cupshelpers.getPrinters(self.cups)
  341.  
  342.                 # Get default printer.
  343.                 try:
  344.                     self.default_printer = self.cups.getDefault ()
  345.                 except AttributeError: # getDefault appeared in pycups-1.9.31
  346.                     # This fetches the list of printers and classes *again*,
  347.                     # just to find out the default printer.
  348.                     dests = self.cups.getDests ()
  349.                     if dests.has_key ((None,None)):
  350.                         self.default_printer = dests[(None,None)].name
  351.                     else:
  352.                         self.default_printer = None
  353.             except cups.IPPError, (e, m):
  354.                 self.show_IPP_Error(e, m)
  355.                 self.printers = {}
  356.                 self.default_printer = None
  357.         else:
  358.             self.printers = {}
  359.             self.default_printer = None
  360.  
  361.         local_printers = []
  362.         local_classes = []
  363.         remote_printers = []
  364.         remote_classes = []
  365.  
  366.         for name, printer in self.printers.iteritems():
  367.             if printer.default:
  368.                 self.default_printer = name
  369.             self.servers.add(printer.getServer())
  370.  
  371.             if printer.remote:
  372.                 if printer.is_class: remote_classes.append(name)
  373.                 else: remote_printers.append(name)
  374.             else:
  375.                 if printer.is_class: local_classes.append(name)
  376.                 else: local_printers.append(name)
  377.  
  378.         local_printers.sort()
  379.         local_classes.sort()
  380.         remote_printers.sort()
  381.         remote_classes.sort()
  382.  
  383.         if (old_name != "" and
  384.             (not old_name in local_printers) and
  385.             (not old_name in local_classes) and
  386.             (not old_name in remote_printers) and
  387.             (not old_name in remote_classes)):
  388.             # The previously selected printer no longer exists.
  389.             old_name = ""
  390.  
  391.         if (self.default_printer != None and
  392.             start_printer == None and
  393.             old_name == ""):
  394.             start_printer = self.default_printer
  395.  
  396.         if not start_printer:
  397.             start_printer = old_name
  398.  
  399.         expanded = {
  400.             "_printers" : True,
  401.             "_classes" : True,
  402.             "_remote_printers" : True,
  403.             "_remote_classes" : True,
  404.             }
  405.  
  406.         """
  407.         # remove old printers/classes
  408.         iter = self.mainlist.get_iter_first()
  409.         iter = self.mainlist.iter_next(iter) # step over server settings
  410.         while iter:
  411.             entry = self.mainlist.get_value(iter, 1)
  412.             path = self.mainlist.get_path(iter)
  413.             expanded[entry] = self.tvMainList.row_expanded(path)
  414.             more_entries =  self.mainlist.remove(iter)
  415.             if not more_entries: break
  416.         """
  417.         self.mainlist.clear()
  418.         QTreeWidgetItem(self.mainlist, ["New Printer", 'New'])
  419.         self.settingsItem = QTreeWidgetItem(self.mainlist, ["Server Settings", 'Settings'])
  420.  
  421.         # add new
  422.         for printers, text, name in (
  423.             (local_printers, _("Local Printers"), "_printers"),
  424.             (local_classes, _("Local Classes"), "_classes"),
  425.             (remote_printers, _("Remote Printers"), "_remote_printers"),
  426.             (remote_classes, _("Remote Classes"), "_remote_classes")):
  427.             if not printers: continue
  428.  
  429.             #self.mainlist.addTopLevelItem(QTreeWidgetItem(self.mainlist, text))
  430.             rootTreeItem = QTreeWidgetItem(self.mainlist, [text, name])
  431.             #iter = self.mainlist.append(None, (text, name))
  432.             #path = self.mainlist.get_path(iter)
  433.  
  434.             for printer_name in printers:
  435.                 if start_printer == None:
  436.                     start_printer = printer_name
  437.                 treeItem = QTreeWidgetItem(rootTreeItem, [printer_name, "Printer"])
  438.                 #p_iter = self.mainlist.append(iter, (printer_name, "Printer"))
  439.                 if printer_name==start_printer:
  440.                     treeItem.setSelected(True)
  441.                     expanded[name] = True
  442.             if expanded[name]:
  443.                 rootTreeItem.setExpanded(True)
  444.                 #self.tvMainList.expand_row(path, False)
  445.         self.on_tvMainList_cursor_changed()
  446.         self.setDataButtonState()
  447.  
  448.         """FIXME
  449.         if change_ppd:
  450.             self.on_btnChangePPD_clicked (self.btnChangePPD)
  451.         """
  452.  
  453.     #TODO
  454.     # Connect to Server
  455.  
  456.     def on_printer_changed(self, text):
  457.         widget = self.sender()
  458.         if not widget:  #method called as a method not a slot
  459.             return
  460.         if isinstance(widget, QCheckBox):
  461.             value = widget.isChecked()
  462.         elif isinstance(widget, QLineEdit):
  463.             value = unicode(widget.text())
  464.         elif isinstance(widget, QRadioButton):
  465.             value = widget.isChecked()
  466.         elif isinstance(widget, QComboBox):
  467.             value = unicode(widget.currentText())
  468.         else:
  469.             raise ValueError, "Widget type not supported (yet)"
  470.  
  471.         p = self.printer
  472.         old_values = {
  473.             self.entPDescription : p.info,
  474.             self.entPLocation : p.location,
  475.             self.entPDevice : p.device_uri,
  476.             self.chkPEnabled : p.enabled,
  477.             self.chkPAccepting : not p.rejecting,
  478.             self.chkPShared : p.is_shared,
  479.             self.cmbPStartBanner : p.job_sheet_start,
  480.             self.cmbPEndBanner : p.job_sheet_end,
  481.             self.cmbPErrorPolicy : p.error_policy,
  482.             self.cmbPOperationPolicy : p.op_policy,
  483.             #self.rbtnPAllow: p.default_allow, #FIXME access control tab
  484.             }
  485.  
  486.         old_value = old_values[widget]
  487.  
  488.         if old_value == value:
  489.             self.changed.discard(widget)
  490.         else:
  491.             self.changed.add(widget)
  492.         self.setDataButtonState()
  493.  
  494.     #TODO
  495.     # Access control
  496.  
  497.     #TODO
  498.     # Server side options
  499.  
  500.     # set buttons sensitivity
  501.     def setDataButtonState(self):
  502.         try: # Might not be a printer selected
  503.             possible = (self.ppd and
  504.                         not bool (self.changed) and
  505.                         self.printer.enabled and
  506.                         not self.printer.rejecting)
  507.  
  508.             self.btnPrintTestPage.setEnabled(possible)
  509.  
  510.             commands = (self.printer.type & cups.CUPS_PRINTER_COMMANDS) != 0
  511.             self.btnSelfTest.setEnabled(commands and possible)
  512.             self.btnCleanHeads.setEnabled(commands and possible)
  513.         except:
  514.             debugprint ("exception in setDataButtonState")
  515.             pass
  516.  
  517.         installablebold = False
  518.         optionsbold = False
  519.         if self.conflicts:
  520.             debugprint ("Conflicts detected")
  521.             self.btnConflict.show()
  522.             for option in self.conflicts:
  523.                 if option.tab_label == self.lblPInstallOptions:
  524.                     installablebold = True
  525.                 else:
  526.                     optionsbold = True
  527.         else:
  528.             self.btnConflict.hide()
  529.         installabletext = _("Installable Options")
  530.         optionstext = _("Printer Options")
  531.         if installablebold:
  532.             installabletext = "<b>%s</b>" % installabletext
  533.         if optionsbold:
  534.             optionstext = "<b>%s</b>" % optionstext
  535.         self.lblPInstallOptions.setText(installabletext)
  536.         self.lblPOptions.setText(optionstext)
  537.  
  538.         """  FIXME
  539.         store = self.tvPrinterProperties.get_model ()
  540.         if store:
  541.             for n in range (self.ntbkPrinter.get_n_pages ()):
  542.                 page = self.ntbkPrinter.get_nth_page (n)
  543.                 label = self.ntbkPrinter.get_tab_label (page)
  544.                 if label == self.lblPInstallOptions:
  545.                     iter = store.get_iter ((n,))
  546.                     store.set_value (iter, 0, installabletext)
  547.                 elif label == self.lblPOptions:
  548.                     iter = store.get_iter ((n,))
  549.                     store.set_value (iter, 0, optionstext)
  550.         """
  551.  
  552.         self.btnPrinterPropertiesApply.setEnabled(len (self.changed) > 0)
  553.         self.btnRevert.setEnabled(len (self.changed) > 0)
  554.  
  555.     def save_printer(self, printer, saveall=False):
  556.         class_deleted = False
  557.         name = printer.name
  558.  
  559.         try:
  560.             if not printer.is_class and self.ppd: 
  561.                 self.getPrinterSettings()
  562.                 if self.ppd.nondefaultsMarked() or saveall:
  563.                     self.passwd_retry = False # use cached Passwd 
  564.                     self.cups.addPrinter(name, ppd=self.ppd)
  565.  
  566.             #FIXME classes
  567.             if printer.is_class:
  568.                 pass
  569.                 """
  570.                 # update member list
  571.                 new_members = self.getCurrentClassMembers(self.tvClassMembers)
  572.                 if not new_members:
  573.                     dialog = gtk.MessageDialog(
  574.                         flags=0, type=gtk.MESSAGE_WARNING,
  575.                         buttons=gtk.BUTTONS_YES_NO,
  576.                         message_format=_("This will delete this class!"))
  577.                     dialog.format_secondary_text(_("Proceed anyway?"))
  578.                     result = dialog.run()
  579.                     dialog.destroy()
  580.                     if result==gtk.RESPONSE_NO:
  581.                         return True
  582.                     class_deleted = True
  583.  
  584.                 # update member list
  585.                 old_members = printer.class_members[:]
  586.  
  587.                 for member in new_members:
  588.                     if member in old_members:
  589.                         old_members.remove(member)
  590.                     else:
  591.                         self.cups.addPrinterToClass(member, name)
  592.                 for member in old_members:
  593.                     self.cups.deletePrinterFromClass(member, name)    
  594.                 """
  595.  
  596.             location = unicode(self.entPLocation.text())
  597.             info = unicode(self.entPDescription.text())
  598.             device_uri = unicode(self.entPDevice.text())
  599.             if device_uri.find (ellipsis) != -1:
  600.                 # The URI is sanitized and not editable.
  601.                 device_uri = printer.device_uri
  602.  
  603.             enabled = self.chkPEnabled.isChecked()
  604.             accepting = self.chkPAccepting.isChecked()
  605.             shared = self.chkPShared.isChecked()
  606.  
  607.             if info!=printer.info or saveall:
  608.                 self.passwd_retry = False # use cached Passwd 
  609.                 self.cups.setPrinterInfo(name, info)
  610.             if location!=printer.location or saveall:
  611.                 self.passwd_retry = False # use cached Passwd 
  612.                 self.cups.setPrinterLocation(name, location)
  613.             if (not printer.is_class and
  614.                 (device_uri!=printer.device_uri or saveall)):
  615.                 self.passwd_retry = False # use cached Passwd 
  616.                 self.cups.setPrinterDevice(name, device_uri)
  617.  
  618.             if enabled != printer.enabled or saveall:
  619.                 self.passwd_retry = False # use cached Passwd 
  620.                 self.printer.setEnabled(enabled)
  621.             if accepting == printer.rejecting or saveall:
  622.                 self.passwd_retry = False # use cached Passwd 
  623.                 self.printer.setAccepting(accepting)
  624.             if shared != printer.is_shared or saveall:
  625.                 self.passwd_retry = False # use cached Passwd 
  626.                 self.printer.setShared(shared)
  627.  
  628.             job_sheet_start = unicode(self.cmbPStartBanner.currentText())
  629.             job_sheet_end = unicode(self.cmbPEndBanner.currentText())
  630.             error_policy = unicode(self.cmbPErrorPolicy.currentText())
  631.             op_policy = unicode(self.cmbPOperationPolicy.currentText())
  632.  
  633.             if (job_sheet_start != printer.job_sheet_start or
  634.                 job_sheet_end != printer.job_sheet_end) or saveall:
  635.                 self.passwd_retry = False # use cached Passwd
  636.                 printer.setJobSheets(job_sheet_start, job_sheet_end)
  637.             if error_policy != printer.error_policy or saveall:
  638.                 self.passwd_retry = False # use cached Passwd
  639.                 printer.setErrorPolicy(error_policy)
  640.             if op_policy != printer.op_policy or saveall:
  641.                 self.passwd_retry = False # use cached Passwd
  642.                 printer.setOperationPolicy(op_policy)
  643.  
  644.             """FIXME TODO access
  645.             default_allow = self.rbtnPAllow.get_active()
  646.             except_users = self.getPUsers()
  647.  
  648.             if (default_allow != printer.default_allow or
  649.                 except_users != printer.except_users) or saveall:
  650.                 self.passwd_retry = False # use cached Passwd
  651.                 printer.setAccess(default_allow, except_users)
  652.  
  653.             for option in printer.attributes:
  654.                 if option not in self.server_side_options:
  655.                     printer.unsetOption(option)
  656.             for option in self.server_side_options.itervalues():
  657.                 if option.is_changed() or saveall:
  658.                     printer.setOption(option.name, option.get_current_value())
  659.             """
  660.         except cups.IPPError, (e, s):
  661.             self.show_IPP_Error(e, s)
  662.             return True
  663.         self.changed = set() # of options
  664.         if not self.__dict__.has_key ("server_settings"):
  665.             # We can authenticate with the server correctly at this point,
  666.             # but we have never fetched the server settings to see whether
  667.             # the server is publishing shared printers.  Fetch the settings
  668.             # now so that we can update the "not published" label if necessary.
  669.             try:
  670.                 self.server_settings = self.cups.adminGetServerSettings()
  671.             except:
  672.                 nonfatalException()
  673.  
  674.         if class_deleted:
  675.             self.populateList ()
  676.         else:
  677.             # Update our copy of the printer's settings.
  678.             printers = cupshelpers.getPrinters (self.cups)
  679.             this_printer = { name: printers[name] }
  680.             self.printers.update (this_printer)
  681.  
  682.         return False
  683.  
  684.     def getPrinterSettings(self):
  685.         #self.ppd.markDefaults()
  686.         for option in self.options.itervalues():
  687.             option.writeback()
  688.  
  689.     @pyqtSignature("")
  690.     def on_btnPrintTestPage_clicked(self):
  691.         if self.test_button_cancels:
  692.             jobs = self.printer.testsQueued ()
  693.             for job in jobs:
  694.                 debugprint ("Canceling job %s" % job)
  695.                 try:
  696.                     self.cups.cancelJob (job)
  697.                 except cups.IPPError, (e, msg):
  698.                     self.show_IPP_Error(e, msg)
  699.             self.setTestButton (self.printer)
  700.             return
  701.         try:
  702.             # if we have a page size specific custom test page, use it;
  703.             # otherwise use cups' default one
  704.             custom_testpage = None
  705.             opt = self.ppd.findOption ("PageSize")
  706.             if opt:
  707.                 custom_testpage = os.path.join(pkgdata, 'testpage-%s.ps' % opt.defchoice.lower())
  708.  
  709.             if custom_testpage and os.path.exists(custom_testpage):
  710.                 debugprint ('Printing custom test page ' + custom_testpage)
  711.                 job_id = self.cups.printTestPage(self.printer.name,
  712.                     file=custom_testpage)
  713.             else:
  714.                 debugprint ('Printing default test page')
  715.                 job_id = self.cups.printTestPage(self.printer.name)
  716.  
  717.             self.setTestButton (self.printer)
  718.             QMessageBox.information(self, _("Submitted"), _("Test page submitted as "
  719.                                                             "job %d") % job_id)
  720.         except cups.IPPError, (e, msg):
  721.             if (e == cups.IPP_NOT_AUTHORIZED and
  722.                 self.connect_server != 'localhost' and
  723.                 self.connect_server[0] != '/'):
  724.                 self.lblError.set_markup ('<span weight="bold" size="larger">'+
  725.                                           _("Not possible") + '</span>\n\n' +
  726.                                           _("The remote server did not accept "
  727.                                             "the print job, most likely "
  728.                                             "because the printer is not "
  729.                                             "shared."))
  730.                 self.ErrorDialog.set_transient_for (self.MainWindow)
  731.                 self.ErrorDialog.run ()
  732.                 self.ErrorDialog.hide ()
  733.             else:
  734.                 self.show_IPP_Error(e, msg)
  735.  
  736.     def maintenance_command (self, command):
  737.         (tmpfd, tmpfname) = tempfile.mkstemp ()
  738.         os.write (tmpfd, "#CUPS-COMMAND\n%s\n" % command)
  739.         os.close (tmpfd)
  740.         try:
  741.             format = "application/vnd.cups-command"
  742.             job_id = self.cups.printTestPage (self.printer.name,
  743.                                               format=format,
  744.                                               file=tmpfname,
  745.                                               user=self.connect_user)
  746.             self.lblInfo.set_markup ('<span weight="bold" size="larger">' +
  747.                                      _("Submitted") + '</span>\n\n' +
  748.                                      _("Maintenance command submitted as "
  749.                                        "job %d") % job_id)
  750.             self.InfoDialog.set_transient_for (self.MainWindow)
  751.             self.InfoDialog.run ()
  752.             self.InfoDialog.hide ()
  753.         except cups.IPPError, (e, msg):
  754.             if (e == cups.IPP_NOT_AUTHORIZED and
  755.                 self.printer.name != 'localhost'):
  756.                 self.lblError.set_markup ('<span weight="bold" size="larger">'+
  757.                                           _("Not possible") + '</span>\n\n' +
  758.                                           _("THe remote server did not accept "
  759.                                             "the print job, most likely "
  760.                                             "because the printer is not "
  761.                                             "shared."))
  762.                 self.ErrorDialog.set_transient_for (self.MainWindow)
  763.                 self.ErrorDialog.run ()
  764.                 self.ErrorDialog.hide ()
  765.             else:
  766.                 self.show_IPP_Error(e, msg)
  767.  
  768.     @pyqtSignature("")
  769.     def on_btnSelfTest_clicked(self):
  770.         self.maintenance_command ("PrintSelfTestPage")
  771.  
  772.     @pyqtSignature("")
  773.     def on_btnCleanHeads_clicked(self):
  774.         self.maintenance_command ("Clean all")
  775.  
  776.     def fillComboBox(self, combobox, values, value):
  777.         combobox.clear()
  778.         for nr, val in enumerate(values):
  779.             combobox.addItem(val)
  780.             if val == value: 
  781.                 combobox.setCurrentIndex(nr)
  782.  
  783.     def fillPrinterTab(self, name):
  784.         self.changed = set() # of options
  785.         self.options = {} # keyword -> Option object
  786.         self.conflicts = set() # of options
  787.  
  788.         printer = self.printers[name] 
  789.         self.printer = printer
  790.         printer.getAttributes ()
  791.  
  792.         editable = not self.printer.discovered
  793.         editablePPD = not self.printer.remote
  794.  
  795.         try:
  796.             self.ppd = printer.getPPD()
  797.         except cups.IPPError, (e, m):
  798.             # Some IPP error other than IPP_NOT_FOUND.
  799.             self.show_IPP_Error(e, m)
  800.             # Treat it as a raw queue.
  801.             self.ppd = False
  802.         except RuntimeError:
  803.             # The underlying cupsGetPPD2() function returned NULL without
  804.             # setting an IPP error, so it'll be something like a failed
  805.             # connection.
  806.             #FIXME show a dialogue
  807.             debugprint("Error!")
  808.             """
  809.             self.lblError.set_markup('<span weight="bold" size="larger">' +
  810.                                      _("Error") + '</span>\n\n' +
  811.                                      _("There was a problem connecting to "
  812.                                        "the CUPS server."))
  813.             self.ErrorDialog.set_transient_for(self.MainWindow)
  814.             self.ErrorDialog.run()
  815.             self.ErrorDialog.hide()
  816.             """
  817.             raise
  818.  
  819.         for widget in (self.entPDescription, self.entPLocation,
  820.                        self.entPDevice):
  821.             widget.setReadOnly(not editable)
  822.  
  823.         for widget in (self.btnSelectDevice, self.btnChangePPD):
  824.             """,FIXME
  825.                        self.chkPEnabled, self.chkPAccepting, self.chkPShared,
  826.                        self.cmbPStartBanner, self.cmbPEndBanner,
  827.                        self.cmbPErrorPolicy, self.cmbPOperationPolicy,
  828.                        self.rbtnPAllow, self.rbtnPDeny, self.tvPUsers,
  829.                        self.entPUser, self.btnPAddUser, self.btnPDelUser):
  830.             """
  831.             widget.setEnabled(editable)
  832.  
  833.         # Description page
  834.         self.entPDescription.setText(printer.info)
  835.         self.entPLocation.setText(printer.location)
  836.         #obsolete self.lblPMakeModel.setText(printer.make_and_model)
  837.         #obsolete self.lblPState.setText(printer.state_description)
  838.  
  839.         uri = printer.device_uri
  840.         if uri.startswith("smb://"):
  841.             (group, host, share,
  842.              user, password) = SMBURI (uri=uri[6:]).separate ()
  843.             if password:
  844.                 uri = "smb://"
  845.                 if len (user) or len (password):
  846.                     uri += ellipsis
  847.                 uri += SMBURI (group=group, host=host, share=share).get_uri ()
  848.                 self.entPDevice.setEnabled(False)
  849.             else:
  850.                 self.entPDevice.setEnabled(True)
  851.         self.entPDevice.setText(uri)
  852.         self.changed.discard(self.entPDevice)
  853.  
  854.         # Hide make/model and Device URI for classes
  855.         for widget in (self.lblPMakeModel2, self.lblPMakeModel,
  856.                        self.btnChangePPD, self.lblPDevice2,
  857.                        self.entPDevice, self.btnSelectDevice):
  858.             if printer.is_class:
  859.                 widget.hide()
  860.             else:
  861.                 widget.show()
  862.  
  863.  
  864.         # default printer
  865.         self.btnPMakeDefault.setEnabled(not printer.default)
  866.         if printer.default:
  867.             self.lblPDefault.setText(_("This is the default printer"))
  868.         elif self.default_printer:
  869.             self.lblPDefault.setText(self.default_printer)
  870.         else:
  871.             self.lblPDefault.setText(_("No default printer set."))
  872.  
  873.         self.setTestButton (printer)
  874.  
  875.         # Policy tab
  876.         # ----------
  877.  
  878.         # State
  879.         self.chkPEnabled.setChecked(printer.enabled)
  880.         self.chkPAccepting.setChecked(not printer.rejecting)
  881.         self.chkPShared.setChecked(printer.is_shared)
  882.         try:
  883.             if printer.is_shared:
  884.                 flag = cups.CUPS_SERVER_SHARE_PRINTERS
  885.                 publishing = int (self.server_settings[flag])
  886.                 if publishing:
  887.                     self.lblNotPublished.hide()
  888.                 else:
  889.                     self.lblNotPublished.show()
  890.             else:
  891.                 self.lblNotPublished.hide()
  892.         except:
  893.             self.lblNotPublished.hide()
  894.  
  895.         # Job sheets
  896.         self.cmbPStartBanner.setEnabled(editable)
  897.         self.cmbPEndBanner.setEnabled(editable)
  898.  
  899.         # Policies
  900.         self.cmbPErrorPolicy.setEnabled(editable)
  901.         self.cmbPOperationPolicy.setEnabled(editable)
  902.  
  903.         """
  904.         # Access control
  905.         self.rbtnPAllow.set_active(printer.default_allow)
  906.         self.rbtnPDeny.set_active(not printer.default_allow)
  907.         self.setPUsers(printer.except_users)
  908.  
  909.         self.entPUser.set_text("")
  910.  
  911.         # Server side options (Job options)
  912.         self.server_side_options = {}
  913.         for option in self.job_options_widgets.values ():
  914.             if option.name == "media" and self.ppd:
  915.                 # Slightly special case because the 'system default'
  916.                 # (i.e. what you get when you press Reset) depends
  917.                 # on the printer's PageSize.
  918.                 opt = self.ppd.findOption ("PageSize")
  919.                 if opt:
  920.                     option.set_default (opt.defchoice)
  921.  
  922.             option_editable = editable
  923.             try:
  924.                 value = self.printer.attributes[option.name]
  925.             except KeyError:
  926.                 option.reinit (None)
  927.             else:
  928.                 try:
  929.                     if self.printer.possible_attributes.has_key (option.name):
  930.                         supported = self.printer.\
  931.                                     possible_attributes[option.name][1]
  932.                         # Set the option widget.
  933.                         # In CUPS 1.3.x the orientation-requested-default
  934.                         # attribute may have the value None; this means there
  935.                         # is no value set.  This suits our needs here, as None
  936.                         # resets the option to the system default and makes the
  937.                         # Reset button insensitive.
  938.                         option.reinit (value, supported=supported)
  939.                     else:
  940.                         option.reinit (value)
  941.  
  942.                     self.server_side_options[option.name] = option
  943.                 except:
  944.                     option_editable = False
  945.                     self.lblError.set_markup ('<span weight="bold" ' +
  946.                                               'size="larger">' +
  947.                                               _("Error") + '</span>\n\n' +
  948.                                               _("Option '%s' has value '%s' "
  949.                                                 "and cannot be edited.") %
  950.                                               (option.name, value))
  951.                     self.ErrorDialog.set_transient_for (self.MainWindow)
  952.                     self.ErrorDialog.run()
  953.                     self.ErrorDialog.hide()
  954.             option.widget.set_sensitive (option_editable)
  955.             if not editable:
  956.                 option.button.set_sensitive (False)
  957.         self.other_job_options = []
  958.         self.draw_other_job_options (editable=editable)
  959.         for option in self.printer.attributes.keys ():
  960.             if self.server_side_options.has_key (option):
  961.                 continue
  962.             supported = ""
  963.             if self.printer.possible_attributes.has_key (option):
  964.                 supported = self.printer.possible_attributes[option][1]
  965.             self.add_job_option (option, value=self.printer.attributes[option],
  966.                                  supported=supported, is_new=False,
  967.                                  editable=editable)
  968.         self.entNewJobOption.set_text ('')
  969.         self.entNewJobOption.set_sensitive (editable)
  970.         self.btnNewJobOption.set_sensitive (False)
  971.  
  972.         if printer.is_class:
  973.             # remove InstallOptions tab
  974.             tab_nr = self.ntbkPrinter.page_num(self.swPInstallOptions)
  975.             if tab_nr != -1:
  976.                 self.ntbkPrinter.remove_page(tab_nr)
  977.             self.fillClassMembers(name, editable)
  978.         else:
  979.             # real Printer
  980.             self.fillPrinterOptions(name, editablePPD)
  981.  
  982.         """
  983.         self.changed = set() # of options
  984.         self.updatePrinterProperties ()
  985.         self.setDataButtonState()
  986.  
  987.     def fillPrinterOptions(self):
  988.         return #FIXME TODO options tabs
  989.  
  990.     #In Gnome is now on_delete_activate(self, UNUSED):
  991.     @pyqtSignature("")
  992.     def on_btnDelete_clicked(self):
  993.         name, type = self.getSelectedItem()
  994.         print name
  995.  
  996.         # Confirm
  997.         if type == "Printer":
  998.             message_format = _("Really delete printer %s?")
  999.         else:
  1000.             message_format = _("Really delete class %s?")
  1001.  
  1002.         cancel = QMessageBox.question(self,"",
  1003.                 message_format % name,
  1004.                 _("&Yes"), _("&No"),
  1005.                 QString(), 0, 1)
  1006.  
  1007.         if cancel:
  1008.             return
  1009.         try:
  1010.             self.cups.deletePrinter(name)
  1011.         except cups.IPPError, (e, msg):
  1012.             self.show_IPP_Error(e, msg)
  1013.  
  1014.         self.changed = set()
  1015.         self.populateList()
  1016.         self.mainlist.setCurrentItem(self.mainlist.itemAt(0,0))
  1017.  
  1018.     #in Gnome side is now  set_default_printer (self, name):
  1019.     @pyqtSignature("")
  1020.     def on_btnPMakeDefault_clicked(self):
  1021.         try:
  1022.             self.cups.setDefault(self.printer.name)
  1023.         except cups.IPPError, (e, msg):
  1024.             self.show_IPP_Error(e, msg)
  1025.             return
  1026.  
  1027.         # Also need to check system-wide lpoptions because that's how
  1028.         # previous Fedora versions set the default (bug #217395).
  1029.         (tmpfd, tmpfname) = tempfile.mkstemp ()
  1030.         success = False
  1031.         try:
  1032.             resource = "/admin/conf/lpoptions"
  1033.             self.cups.getFile(resource, tmpfname)
  1034.             #success = True
  1035.         except cups.HTTPError, (s,):
  1036.             try:
  1037.                 os.remove (tmpfname)
  1038.             except OSError:
  1039.                 pass
  1040.  
  1041.             if s != cups.HTTP_NOT_FOUND:
  1042.                 self.show_HTTP_Error(s)
  1043.                 return
  1044.  
  1045.         if success:
  1046.             lines = file (tmpfname).readlines ()
  1047.             changed = False
  1048.             i = 0
  1049.             for line in lines:
  1050.                 if line.startswith ("Default "):
  1051.                     # This is the system-wide default.
  1052.                     name = line.split (' ')[1]
  1053.                     if name != self.printer.name:
  1054.                         # Stop it from over-riding the server default.
  1055.                         lines[i] = "Dest " + line[8:]
  1056.                         changed = True
  1057.                 i += 1
  1058.  
  1059.             if changed:
  1060.                 file (tmpfname, 'w').writelines (lines)
  1061.                 try:
  1062.                     self.cups.putFile (resource, tmpfname)
  1063.                 except cups.HTTPError, (s,):
  1064.                     os.remove (tmpfname)
  1065.                     print s
  1066.                     self.show_HTTP_Error(s)
  1067.                     return
  1068.  
  1069.                 # Now reconnect because the server needs to reload.
  1070.                 self.reconnect ()
  1071.  
  1072.         try:
  1073.             os.remove (tmpfname)
  1074.         except OSError:
  1075.             pass
  1076.  
  1077.         try:
  1078.             self.populateList()
  1079.         except cups.HTTPError, (s,):
  1080.             self.cups = None
  1081.             self.setConnected()
  1082.             self.populateList()
  1083.             self.show_HTTP_Error(s)
  1084.  
  1085.     ##########################################################################
  1086.     ### Server settings
  1087.     ##########################################################################
  1088.  
  1089.     def fillServerTab(self):
  1090.         self.changed = set()
  1091.         try:
  1092.             self.server_settings = self.cups.adminGetServerSettings()
  1093.         except cups.IPPError, (e, m):
  1094.             #FIXME
  1095.             self.show_IPP_Error(e, m)
  1096.             self.tvMainList.get_selection().unselect_all()
  1097.             self.on_tvMainList_cursor_changed(self.tvMainList)
  1098.             return
  1099.  
  1100.         for widget, setting in [
  1101.             (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS),
  1102.             (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS),
  1103.             (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY),
  1104.             (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN),
  1105.             (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY),
  1106.             (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING),]:
  1107.             # widget.set_data("setting", setting)
  1108.             self.widget_data_setting[widget] = setting
  1109.             self.disconnect(widget, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  1110.             if self.server_settings.has_key(setting):
  1111.                 widget.setChecked(int(self.server_settings[setting]))
  1112.                 widget.setEnabled(True)
  1113.             else:
  1114.                 widget.setChecked(False)
  1115.                 widget.setEnabled(False)
  1116.             self.connect(widget, SIGNAL("stateChanged(int)"), self.on_server_widget_changed)
  1117.         self.setDataButtonState()
  1118.         # Set sensitivity of 'Allow printing from the Internet'.
  1119.         self.on_server_changed (self.chkServerShare) # (any will do here)
  1120.  
  1121.     def on_server_widget_changed(self, value):
  1122.         self.on_server_changed(self.sender())
  1123.  
  1124.     def on_server_changed(self, widget):
  1125.         #setting = widget.get_data("setting")
  1126.         #print "widget_data_setting " + str(self.widget_data_setting)
  1127.         #print "widget " + str(widget)
  1128.         setting = self.widget_data_setting[widget]
  1129.         if self.server_settings.has_key (setting):
  1130.             if str(int(widget.isChecked())) == self.server_settings[setting]:
  1131.                 self.changed.discard(widget)
  1132.             else:
  1133.                 self.changed.add(widget)
  1134.  
  1135.         sharing = self.chkServerShare.isChecked ()
  1136.         self.chkServerShareAny.setEnabled (
  1137.             sharing and self.server_settings.has_key(try_CUPS_SERVER_REMOTE_ANY))
  1138.  
  1139.         self.setDataButtonState()
  1140.  
  1141.     def save_serversettings(self):
  1142.         setting_dict = self.server_settings.copy()
  1143.         for widget, setting in [
  1144.             (self.chkServerBrowse, cups.CUPS_SERVER_REMOTE_PRINTERS),
  1145.             (self.chkServerShare, cups.CUPS_SERVER_SHARE_PRINTERS),
  1146.             (self.chkServerShareAny, try_CUPS_SERVER_REMOTE_ANY),
  1147.             (self.chkServerRemoteAdmin, cups.CUPS_SERVER_REMOTE_ADMIN),
  1148.             (self.chkServerAllowCancelAll, cups.CUPS_SERVER_USER_CANCEL_ANY),
  1149.             (self.chkServerLogDebug, cups.CUPS_SERVER_DEBUG_LOGGING),]:
  1150.             if not self.server_settings.has_key(setting): continue
  1151.             setting_dict[setting] = str(int(widget.isChecked()))
  1152.         try:
  1153.             self.cups.adminSetServerSettings(setting_dict)
  1154.         except cups.IPPError, (e, m):
  1155.             self.show_IPP_Error(e, m)
  1156.             return True
  1157.         except RuntimeError, s:
  1158.             self.show_IPP_Error(None, s)
  1159.             return True
  1160.         self.changed = set()
  1161.         self.setDataButtonState()
  1162.         time.sleep(1) # give the server a chance to process our request
  1163.  
  1164.         # Now reconnect, in case the server needed to reload.
  1165.         self.reconnect ()
  1166.  
  1167.         # Refresh the server settings in case they have changed in the
  1168.         # mean time.
  1169.         try:
  1170.             self.fillServerTab()
  1171.         except:
  1172.             nonfatalException()
  1173.  
  1174.     # ====================================================================
  1175.     # == New Printer Dialog ==============================================
  1176.     # ====================================================================
  1177.  
  1178.     # new printer
  1179.     def on_new_printer_activate(self):
  1180.         self.busy()
  1181.         self.newPrinterGUI.init("printer")
  1182.         self.ready()
  1183.  
  1184.     # new class
  1185.     def on_new_class_activate(self):
  1186.         self.newPrinterGUI.init("class")
  1187.  
  1188.     @pyqtSignature("")
  1189.     def on_btnSelectDevice_clicked(self):
  1190.         self.newPrinterGUI.init("device")
  1191.  
  1192.     @pyqtSignature("")
  1193.     def on_btnChangePPD_clicked(self):
  1194.         self.busy(self)
  1195.         self.newPrinterGUI.init("ppd")
  1196.         self.ready(self)
  1197.  
  1198.     def checkNPName(self, name):
  1199.         if not name: return False
  1200.         name = name.lower()
  1201.         for printer in self.printers.values():
  1202.             if not printer.discovered and printer.name.lower()==name:
  1203.                 return False
  1204.         return True
  1205.  
  1206.     def makeNameUnique(self, name):
  1207.         """Make a suggested queue name valid and unique."""
  1208.         name = name.replace (" ", "_")
  1209.         name = name.replace ("/", "_")
  1210.         name = name.replace ("#", "_")
  1211.         if not self.checkNPName (name):
  1212.             suffix=2
  1213.             while not self.checkNPName (name + str (suffix)):
  1214.                 suffix += 1
  1215.                 if suffix == 100:
  1216.                     break
  1217.             name += str (suffix)
  1218.         return name
  1219.  
  1220.     #TODO
  1221.     ## Watcher interface helpers
  1222.  
  1223.     @pyqtSignature("")
  1224.     def on_btnRevert_clicked(self):
  1225.         self.changed = set() # avoid asking the user
  1226.         self.on_tvMainList_cursor_changed()
  1227.  
  1228.     @pyqtSignature("")
  1229.     def on_btnPrinterPropertiesApply_clicked(self):
  1230.         err = self.printer_properties_response()
  1231.         if not err:
  1232.             self.populateList()
  1233.         else:
  1234.             nonfatalException()
  1235.  
  1236.     def show_IPP_Error(self, exception, message):
  1237.         if exception == cups.IPP_NOT_AUTHORIZED:
  1238.             QMessageBox.critical(self, _('Not authorized'), _('The password may be incorrect.'))
  1239.         else:
  1240.             QMessageBox.critical(self, _('CUPS server error'), _("There was an error during the CUPS "\
  1241.                                                               "operation: '%s'.") % message)
  1242.     def show_HTTP_Error(self, status):
  1243.         if (status == cups.HTTP_UNAUTHORIZED or
  1244.             status == cups.HTTP_FORBIDDEN):
  1245.             QMessageBox.critical(self, _('Not authorized'), 
  1246.                                  _('The password may be incorrect, or the '
  1247.                                    'server may be configured to deny '
  1248.                                    'remote administration.'))
  1249.         else:
  1250.             if status == cups.HTTP_BAD_REQUEST:
  1251.                 msg = _("Bad request")
  1252.             elif status == cups.HTTP_NOT_FOUND:
  1253.                 msg = _("Not found")
  1254.             elif status == cups.HTTP_REQUEST_TIMEOUT:
  1255.                 msg = _("Request timeout")
  1256.             elif status == cups.HTTP_UPGRADE_REQUIRED:
  1257.                 msg = _("Upgrade required")
  1258.             elif status == cups.HTTP_SERVER_ERROR:
  1259.                 msg = _("Server error")
  1260.             elif status == -1:
  1261.                 msg = _("Not connected")
  1262.             else:
  1263.                 msg = _("status %d") % status
  1264.  
  1265.             error_text = ('<span weight="bold" size="larger">' +
  1266.                           _('CUPS server error') + '</span>\n\n' +
  1267.                           _("There was an HTTP error: %s.")) % msg
  1268.         QMessageBox.critical(self, _('CUPS server error'), _("There was an HTTP error: %s.") % msg)
  1269.  
  1270.     def getSelectedItem(self):
  1271.         return str(self.mainListSelectedName).decode ('utf-8'), str(self.mainListSelectedType)
  1272.         """
  1273.         items = self.mainlist.selectedItems()
  1274.         if len(items) < 1:
  1275.             return ("", 'None')
  1276.             item = items[0]
  1277.             name = item.text(0)
  1278.             type = item.text(1)
  1279.             name = str(name).decode ('utf-8')
  1280.             return name.strip(), type
  1281.         """
  1282.  
  1283.     def reconnect (self):
  1284.         """Reconnect to CUPS after the server has reloaded."""
  1285.         # libcups would handle the reconnection if we just told it to
  1286.         # do something, for example fetching a list of classes.
  1287.         # However, our local authentication certificate would be
  1288.         # invalidated by a server restart, so it is better for us to
  1289.         # handle the reconnection ourselves.
  1290.  
  1291.         # Disconnect.
  1292.         self.cups = None
  1293.         self.setConnected()
  1294.  
  1295.         cups.setServer(self.connect_server)
  1296.         cups.setUser(self.connect_user)
  1297.         attempt = 1
  1298.         while attempt <= 5:
  1299.             try:
  1300.                 self.cups = cups.Connection ()
  1301.                 break
  1302.             except RuntimeError:
  1303.                 # Connection failed.
  1304.                 time.sleep(1)
  1305.                 attempt += 1
  1306.  
  1307.         self.setConnected()
  1308.         self.passwd_retry = False
  1309.  
  1310.     def updatePrinterProperties(self):
  1311.         debugprint ("update printer properties")
  1312.         printer = self.printer
  1313.         self.lblPMakeModel.setText(printer.make_and_model)
  1314.         state = self.printer_states.get (printer.state, _("Unknown"))
  1315.         reason = printer.other_attributes.get ('printer-state-message', '')
  1316.         if len (reason) > 0:
  1317.             state += ' - ' + reason
  1318.         self.lblPState.setText(state)
  1319.         if len (self.changed) == 0:
  1320.             debugprint ("no changes yet: full printer properties update")
  1321.             # State
  1322.             self.chkPEnabled.setEnabled(printer.enabled)
  1323.             self.chkPAccepting.setEnabled(not printer.rejecting)
  1324.             self.chkPShared.setEnabled(printer.is_shared)
  1325.  
  1326.             # Job sheets
  1327.             self.fillComboBox(self.cmbPStartBanner,
  1328.                               printer.job_sheets_supported,
  1329.                               printer.job_sheet_start),
  1330.             self.fillComboBox(self.cmbPEndBanner, printer.job_sheets_supported,
  1331.                               printer.job_sheet_end)
  1332.  
  1333.             # Policies
  1334.             self.fillComboBox(self.cmbPErrorPolicy,
  1335.                               printer.error_policy_supported,
  1336.                               printer.error_policy)
  1337.             self.fillComboBox(self.cmbPOperationPolicy,
  1338.                               printer.op_policy_supported,
  1339.                               printer.op_policy)
  1340.  
  1341.             """
  1342.             # Access control
  1343.             self.rbtnPAllow.set_active(printer.default_allow)
  1344.             self.rbtnPDeny.set_active(not printer.default_allow)
  1345.             self.setPUsers(printer.except_users)
  1346.             """
  1347.  
  1348.     def setTestButton (self, printer):
  1349.         if printer.testsQueued ():
  1350.             self.test_button_cancels = True
  1351.             self.btnPrintTestPage.setText(_('Cancel Tests'))
  1352.             self.btnPrintTestPage.setEnabled(True)
  1353.         else:
  1354.             self.test_button_cancels = False
  1355.             self.btnPrintTestPage.setText(_('Print Test Page'))
  1356.             self.setDataButtonState()
  1357.  
  1358.     def getCurrentClassMembers(self, listwidget):
  1359.         count = listwidget.count()
  1360.         result = []
  1361.         for i in range(count):            
  1362.             result.append(listwidget.item(i).text())
  1363.         result.sort()
  1364.         return result
  1365.  
  1366.     def moveClassMembers(self, treeview_from, treeview_to):
  1367.         rows = treeview_from.selectedItems()
  1368.         for row in rows:
  1369.             treeview_from.takeItem(treeview_from.row(row))
  1370.             treeview_to.addItem(row)
  1371.  
  1372.     # Password handling
  1373.  
  1374.     #FIXME obsolete?
  1375.     def cupsPasswdCallback(self, querystring):
  1376.         return "" #FIXME
  1377.         if self.passwd_retry or len(self.password) == 0:
  1378.             waiting = self.WaitWindow.get_property('visible')
  1379.             if waiting:
  1380.                 self.WaitWindow.hide ()
  1381.             self.lblPasswordPrompt.set_label (self.prompt_primary +
  1382.                                               querystring)
  1383.             self.PasswordDialog.set_transient_for (self.MainWindow)
  1384.             self.entPasswd.grab_focus ()
  1385.  
  1386.             result = self.PasswordDialog.run()
  1387.             self.PasswordDialog.hide()
  1388.             if waiting:
  1389.                 self.WaitWindow.show ()
  1390.             while gtk.events_pending ():
  1391.                 gtk.main_iteration ()
  1392.             if result == gtk.RESPONSE_OK:
  1393.                 self.password = self.entPasswd.get_text()
  1394.             else:
  1395.                 self.password = ''
  1396.             self.passwd_retry = False
  1397.         else:
  1398.             self.passwd_retry = True
  1399.         return self.password
  1400.  
  1401. class NewPrinterGUI(QDialog):
  1402.  
  1403.     new_printer_device_tabs = {
  1404.         "parallel" : 0, # empty tab
  1405.         "usb" : 0,
  1406.         "hal" : 0,
  1407.         "beh" : 0,
  1408.         "hp" : 0,
  1409.         "hpfax" : 0,
  1410.         "socket": 2,
  1411.         "ipp" : 3,
  1412.         "http" : 3,
  1413.         "lpd" : 4,
  1414.         "scsi" : 5,
  1415.         "serial" : 6,
  1416.         "smb" : 7,
  1417.         }
  1418.  
  1419.     ntbkNewPrinterPages = {
  1420.         "name" : 0,
  1421.         "device" : 1,
  1422.         "make" : 2,
  1423.         "model" : 3,
  1424.         "class-members" : 4,
  1425.         "downloadable" : -1,
  1426.     }
  1427.  
  1428.     def __init__(self, mainapp):
  1429.         QDialog.__init__(self, mainapp)
  1430.         self.mainapp = mainapp
  1431.         self.language = mainapp.language
  1432.         self.dialog_mode = ""
  1433.  
  1434.         self.WaitWindow = QMessageBox(self.mainapp)
  1435.         self.WaitWindow.setStandardButtons(QMessageBox.NoButton)
  1436.  
  1437.         uic.loadUi(APPDIR + "/" + "new-printer.ui", self)
  1438.  
  1439.         self.connect(self.tvNPDevices, SIGNAL("itemSelectionChanged()"), self.on_tvNPDevices_cursor_changed)
  1440.         self.connect(self.tvNPMakes, SIGNAL("itemSelectionChanged()"), self.on_tvNPMakes_cursor_changed)
  1441.         self.connect(self.tvNPModels, SIGNAL("itemSelectionChanged()"), self.on_tvNPModels_cursor_changed)
  1442.         self.connect(self.entNPTDevice, SIGNAL("textEdited(const QString&)"), self.on_entNPTDevice_changed)
  1443. #        self.connect(self.entNPTIPPHostname, SIGNAL("textEdited(const QString&)"), self.on_entNPTIPPHostname_changed)
  1444. #        self.connect(self.entNPTIPPQueuename, SIGNAL("textEdited(const QString&)"), self.on_entNPTIPPQueuename_changed)
  1445.         self.connect(self.entSMBURI, SIGNAL("textEdited(const QString&)"), self.on_entSMBURI_changed)
  1446.         self.rbtnSMBAuthPrompt.setChecked(True)
  1447.         self.on_rbtnSMBAuthSet_toggled(False)
  1448.         self.connect(self.rbtnSMBAuthSet, SIGNAL("toggled(bool)"), self.on_rbtnSMBAuthSet_toggled)
  1449.         self.rbtnNPFoomatic.setChecked(True)
  1450.         self.connect(self.rbtnNPFoomatic, SIGNAL("toggled(bool)"), self.on_rbtnNPFoomatic_toggled)
  1451.  
  1452.         self.options = {} # keyword -> Option object
  1453.         self.changed = set()
  1454.         self.conflicts = set()
  1455.         self.ppd = None
  1456.  
  1457.         # Synchronisation objects.
  1458.         self.ppds_lock = thread.allocate_lock()
  1459.         self.devices_lock = thread.allocate_lock()
  1460.         self.smb_lock = thread.allocate_lock()
  1461.         self.ipp_lock = thread.allocate_lock()
  1462.         self.drivers_lock = thread.allocate_lock()
  1463.  
  1464.         #self.connect(self.btnNCAddMember, SIGNAL("clicked()"), self.slot_btnNCAddMember_clicked)
  1465.         #self.connect(self.btnNCDelMember, SIGNAL("clicked()"), self.slot_btnNCDelMember_clicked)
  1466.  
  1467.         """
  1468.         # share with mainapp
  1469.         self.WaitWindow = mainapp.WaitWindow
  1470.         self.lblWait = mainapp.lblWait
  1471.         self.busy = mainapp.busy
  1472.         self.ready = mainapp.ready
  1473.         self.show_IPP_Error = mainapp.show_IPP_Error
  1474.         self.show_HTTP_Error = mainapp.show_HTTP_Error
  1475.         """
  1476.  
  1477.         # Optionally disable downloadable driver support.
  1478.         if not config.DOWNLOADABLE_DRIVER_SUPPORT:
  1479.             self.rbtnNPDownloadableDriverSearch.setEnabled(False)
  1480.             self.downloadableDriverSearchFrame.hide()
  1481.  
  1482.         """
  1483.         # Set up OpenPrinting widgets.
  1484.         self.openprinting = openprinting.OpenPrinting ()
  1485.         self.openprinting_query_handle = None
  1486.         combobox = self.cmbNPDownloadableDriverFoundPrinters
  1487.         cell = gtk.CellRendererText()
  1488.         combobox.pack_start (cell, True)
  1489.         combobox.add_attribute(cell, 'text', 0)
  1490.  
  1491.         # SMB browser
  1492.         self.smb_store = gtk.TreeStore (str, # host or share
  1493.                                         str, # comment
  1494.                                         gobject.TYPE_PYOBJECT, # domain dict
  1495.                                         gobject.TYPE_PYOBJECT) # host dict
  1496.         self.tvSMBBrowser.set_model (self.smb_store)
  1497.         self.smb_store.set_sort_column_id (0, gtk.SORT_ASCENDING)
  1498.  
  1499.         # SMB list columns
  1500.         col = gtk.TreeViewColumn (_("Share"), gtk.CellRendererText (),
  1501.                                   text=0)
  1502.         col.set_resizable (True)
  1503.         col.set_sort_column_id (0)
  1504.         self.tvSMBBrowser.append_column (col)
  1505.  
  1506.         col = gtk.TreeViewColumn (_("Comment"), gtk.CellRendererText (),
  1507.                                   text=1)
  1508.         self.tvSMBBrowser.append_column (col)
  1509.         slct = self.tvSMBBrowser.get_selection ()
  1510.         slct.set_select_function (self.smb_select_function)
  1511.  
  1512.         self.SMBBrowseDialog.set_transient_for(self.NewPrinterWindow)
  1513.  
  1514.         # IPP browser
  1515.         self.ipp_store = gtk.TreeStore (str, # queue
  1516.                                         str, # location
  1517.                                         gobject.TYPE_PYOBJECT) # dict
  1518.         self.tvIPPBrowser.set_model (self.ipp_store)
  1519.         self.ipp_store.set_sort_column_id (0, gtk.SORT_ASCENDING)
  1520.  
  1521.         # IPP list columns
  1522.         col = gtk.TreeViewColumn (_("Queue"), gtk.CellRendererText (),
  1523.                                   text=0)
  1524.         col.set_resizable (True)
  1525.         col.set_sort_column_id (0)
  1526.         self.tvIPPBrowser.append_column (col)
  1527.  
  1528.         col = gtk.TreeViewColumn (_("Location"), gtk.CellRendererText (),
  1529.                                   text=1)
  1530.         self.tvIPPBrowser.append_column (col)
  1531.         self.IPPBrowseDialog.set_transient_for(self.NewPrinterWindow)
  1532.  
  1533.         self.tvNPDriversTooltips = TreeViewTooltips(self.tvNPDrivers, self.NPDriversTooltips)
  1534.  
  1535.         ppd_filter = gtk.FileFilter()
  1536.         ppd_filter.set_name(_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)"))
  1537.         ppd_filter.add_pattern("*.ppd")
  1538.         ppd_filter.add_pattern("*.PPD")
  1539.         ppd_filter.add_pattern("*.ppd.gz")
  1540.         ppd_filter.add_pattern("*.PPD.gz")
  1541.         ppd_filter.add_pattern("*.PPD.GZ")
  1542.         self.filechooserPPD.add_filter(ppd_filter)
  1543.  
  1544.         ppd_filter = gtk.FileFilter()
  1545.         ppd_filter.set_name(_("All files (*)"))
  1546.         ppd_filter.add_pattern("*")
  1547.         self.filechooserPPD.add_filter(ppd_filter)
  1548.  
  1549.         self.xml.signal_autoconnect(self)
  1550.         """
  1551.  
  1552.         #FIXME hide bits which are not yet implemented
  1553.         self.btnSMBBrowse.hide()
  1554.         self.btnSMBVerify.hide()
  1555.         self.btnIPPFindQueue.hide()
  1556.         self.btnIPPVerify.hide()
  1557.         self.btnNPTLpdProbe.hide()
  1558.  
  1559.     def option_changed(self, option):
  1560.         if option.is_changed():
  1561.             self.changed.add(option)
  1562.         else:
  1563.             self.changed.discard(option)
  1564.  
  1565.         if option.conflicts:
  1566.             self.conflicts.add(option)
  1567.         else:
  1568.             self.conflicts.discard(option)
  1569.         self.setDataButtonState()
  1570.  
  1571.         return
  1572.  
  1573.     def setDataButtonState(self):
  1574.         self.btnNPForward.setEnabled(not bool(self.conflicts))
  1575.  
  1576.     def init(self, dialog_mode):
  1577.         self.dialog_mode = dialog_mode
  1578.         self.options = {} # keyword -> Option object
  1579.         self.changed = set()
  1580.         self.conflicts = set()
  1581.  
  1582.         """
  1583.         combobox = self.cmbNPDownloadableDriverFoundPrinters
  1584.         combobox.set_model (gtk.ListStore (str, str))
  1585.         self.entNPDownloadableDriverSearch.set_text ('')
  1586.         button = self.btnNPDownloadableDriverSearch
  1587.         label = button.get_children ()[0].get_children ()[0].get_children ()[1]
  1588.         self.btnNPDownloadableDriverSearch_label = label
  1589.         label.set_text (_("Search"))
  1590.         """
  1591.  
  1592.         if self.dialog_mode == "printer":
  1593.             self.setWindowTitle(_("New Printer"))
  1594.             # Start on devices page (1, not 0)
  1595.             self.ntbkNewPrinter.setCurrentIndex(self.ntbkNewPrinterPages["device"])
  1596.             self.fillDeviceTab()
  1597.             self.on_rbtnNPFoomatic_toggled()
  1598.             # Start fetching information from CUPS in the background
  1599.             self.new_printer_PPDs_loaded = False
  1600.             self.queryPPDs ()
  1601.  
  1602.         elif self.dialog_mode == "class":
  1603.             self.setWindowTitle(_("New Class"))
  1604.             self.fillNewClassMembers()
  1605.             # Start on name page
  1606.             self.ntbkNewPrinter.setCurrentIndex(self.ntbkNewPrinterPages["name"])
  1607.         elif self.dialog_mode == "device":
  1608.             self.setWindowTitle(_("Change Device URI"))
  1609.             self.ntbkNewPrinter.setCurrentIndex(self.ntbkNewPrinterPages["device"])
  1610.             self.queryDevices ()
  1611.             self.loadPPDs()
  1612.             self.fillDeviceTab(self.mainapp.printer.device_uri)
  1613.             # Start fetching information from CUPS in the background
  1614.             self.new_printer_PPDs_loaded = False
  1615.             self.queryPPDs ()
  1616.         elif self.dialog_mode == "ppd":
  1617.             self.setWindowTitle(_("Change Driver"))
  1618.             self.ntbkNewPrinter.setCurrentIndex(2)
  1619.             self.on_rbtnNPFoomatic_toggled()
  1620.  
  1621.             self.auto_model = ""
  1622.             ppd = self.mainapp.ppd
  1623.             if ppd:
  1624.                 attr = ppd.findAttr("Manufacturer")
  1625.                 if attr:
  1626.                     self.auto_make = attr.value
  1627.                 else:
  1628.                     self.auto_make = ""
  1629.                 attr = ppd.findAttr("ModelName")
  1630.                 if not attr: attr = ppd.findAttr("ShortNickName")
  1631.                 if not attr: attr = ppd.findAttr("NickName")
  1632.                 if attr:
  1633.                     if attr.value.startswith(self.auto_make):
  1634.                         self.auto_model = attr.value[len(self.auto_make):].strip ()
  1635.                     else:
  1636.                         try:
  1637.                             self.auto_model = attr.value.split(" ", 1)[1]
  1638.                         except IndexError:
  1639.                             self.auto_model = ""
  1640.                 else:
  1641.                     self.auto_model = ""
  1642.             else:
  1643.                 # Special CUPS names for a raw queue.
  1644.                 self.auto_make = 'Raw'
  1645.                 self.auto_model = 'Queue'
  1646.  
  1647.             self.loadPPDs ()
  1648.             self.fillMakeList()
  1649.  
  1650.         if self.dialog_mode in ("printer", "class"):
  1651.             self.entNPName.setText (self.mainapp.makeNameUnique(self.dialog_mode))
  1652.             #FIXMEself.entNPName.grab_focus()
  1653.             for widget in [self.entNPLocation,
  1654.                            self.entNPDescription]: #,
  1655.                            #self.entSMBURI, self.entSMBUsername,
  1656.                            #self.entSMBPassword, self.entNPTDirectJetHostname]:
  1657.                 widget.setText('')
  1658.  
  1659.             try:
  1660.                 p = os.popen ('/bin/hostname', 'r')
  1661.                 hostname = p.read ().strip ()
  1662.                 p.close ()
  1663.                 self.entNPLocation.setText(hostname)
  1664.             except:
  1665.                 nonfatalException ()
  1666.  
  1667.         self.entNPTDirectJetPort.setText('9100')
  1668.         self.setNPButtons()
  1669.         self.exec_()
  1670.  
  1671.     # get PPDs
  1672.  
  1673.     def queryPPDs(self):
  1674.         debugprint ("queryPPDs")
  1675.         if not self.ppds_lock.acquire(0):
  1676.             debugprint ("queryPPDs: in progress")
  1677.             return
  1678.         debugprint ("Lock acquired for PPDs thread")
  1679.         # Start new thread
  1680.         thread.start_new_thread (self.getPPDs_thread, (self.language[0],))
  1681.         debugprint ("PPDs thread started")
  1682.  
  1683.     def getPPDs_thread(self, language):
  1684.         try:
  1685.             debugprint ("Connecting (PPDs)")
  1686.             cups.setServer (self.mainapp.connect_server)
  1687.             cups.setUser (self.mainapp.connect_user)
  1688.             cups.setPasswordCB (self.mainapp.cupsPasswdCallback)
  1689.             # cups.setEncryption (...)
  1690.             c = cups.Connection ()
  1691.             debugprint ("Fetching PPDs")
  1692.             ppds_dict = c.getPPDs()
  1693.             self.ppds_result = cupshelpers.ppds.PPDs(ppds_dict,
  1694.                                                      language=language)
  1695.             debugprint ("Closing connection (PPDs)")
  1696.             del c
  1697.         except cups.IPPError, (e, msg):
  1698.             self.ppds_result = cups.IPPError (e, msg)
  1699.         except:
  1700.             nonfatalException()
  1701.             self.ppds_result = { }
  1702.  
  1703.         debugprint ("Releasing PPDs lock")
  1704.         self.ppds_lock.release ()
  1705.  
  1706.     def fetchPPDs(self, parent=None):
  1707.         debugprint ("fetchPPDs")
  1708.         self.queryPPDs()
  1709.         time.sleep (0.1)
  1710.  
  1711.         # Keep the UI refreshed while we wait for the devices to load.
  1712.         waiting = False
  1713.         while (self.ppds_lock.locked()):
  1714.             if not waiting:
  1715.                 waiting = True
  1716.                 self.WaitWindow.setText('<b>' +
  1717.                                          _('Searching') + '</b><br /><br />' +
  1718.                                          _('Searching for drivers'))
  1719.                 self.WaitWindow.show ()
  1720.  
  1721.             QApplication.processEvents()
  1722.  
  1723.             time.sleep (0.1)
  1724.  
  1725.         if waiting:
  1726.             self.WaitWindow.hide ()
  1727.  
  1728.         debugprint ("Got PPDs")
  1729.         result = self.ppds_result # atomic operation
  1730.         if isinstance (result, cups.IPPError):
  1731.             # Propagate exception.
  1732.             raise result
  1733.         return result
  1734.  
  1735.     def loadPPDs(self, parent=None):
  1736.         try:
  1737.             return self.ppds
  1738.         except:
  1739.             self.ppds = self.fetchPPDs (parent=parent)
  1740.             return self.ppds
  1741.  
  1742.     def dropPPDs(self):
  1743.         try:
  1744.             del self.ppds
  1745.         except:
  1746.             pass
  1747.  
  1748.     # Class members
  1749.  
  1750.     def fillNewClassMembers(self):
  1751.         self.tvNCMembers.clear()
  1752.         self.tvNCNotMembers.clear()
  1753.         for printer in self.mainapp.printers.itervalues():
  1754.             self.tvNCNotMembers.addItem(printer.name)
  1755.  
  1756.     @pyqtSignature("")
  1757.     def on_btnNCAddMember_clicked(self):
  1758.         self.mainapp.moveClassMembers(self.tvNCNotMembers, self.tvNCMembers)
  1759.         self.btnNPApply.setEnabled(
  1760.             bool(self.mainapp.getCurrentClassMembers(self.tvNCMembers)))
  1761.  
  1762.     @pyqtSignature("")
  1763.     def on_btnNCDelMember_clicked(self):
  1764.         self.mainapp.moveClassMembers(self.tvNCMembers, self.tvNCNotMembers)
  1765.         self.btnNPApply.setEnabled(
  1766.             bool(self.mainapp.getCurrentClassMembers(self.tvNCMembers)))
  1767.  
  1768.     @pyqtSignature("")
  1769.     def on_btnNPBack_clicked(self):
  1770.         self.nextNPTab(-1)
  1771.  
  1772.     @pyqtSignature("")
  1773.     def on_btnNPForward_clicked(self):
  1774.         self.nextNPTab()
  1775.  
  1776.     def nextNPTab(self, step=1):
  1777.         page_nr = self.ntbkNewPrinter.currentIndex()
  1778.  
  1779.         if self.dialog_mode == "class":
  1780.             #order = [0, 4, 5]
  1781.             order = [self.ntbkNewPrinterPages["name"], self.ntbkNewPrinterPages["class-members"]]
  1782.         elif self.dialog_mode == "printer":
  1783.             #self.busy(self)
  1784.             if page_nr == 1: # Device (first page)
  1785.                 # Choose an appropriate name.
  1786.                 name = 'printer'
  1787.                 try:
  1788.                     if self.device.id:
  1789.                         name = self.device.id_dict["MDL"]
  1790.                     name = self.mainapp.makeNameUnique (name)
  1791.                     self.entNPName.setText(name)
  1792.                 except:
  1793.                     nonfatalException ()
  1794.                 self.auto_make, self.auto_model = None, None
  1795.                 self.device.uri = self.getDeviceURI()
  1796.                 if self.device.type in ("socket", "lpd", "ipp", "bluetooth"):
  1797.                     host = self.getNetworkPrinterMakeModel(self.device)
  1798.                     faxuri = None
  1799.                     if host:
  1800.                         faxuri = self.get_hplip_uri_for_network_printer(host,
  1801.                                                                         "fax")
  1802.                     if faxuri:
  1803.                         ##FIXME
  1804.                         dialog = gtk.Dialog(self.device.info,
  1805.                                             self.NewPrinterWindow,
  1806.                                             gtk.DIALOG_MODAL |
  1807.                                             gtk.DIALOG_DESTROY_WITH_PARENT,
  1808.                                             (_("Printer"), 1,
  1809.                                              _("Fax"), 2))
  1810.                         label = gtk.Label(_("This printer supports both "
  1811.                                             "printing and sending faxes.  "
  1812.                                             "Which functionality should be "
  1813.                                             "used for this queue?"))
  1814.                         dialog.vbox.pack_start(label, True, True, 0)
  1815.                         label.show()
  1816.                         queue_type = dialog.run()
  1817.                         dialog.destroy()
  1818.                         if (queue_type == 2):
  1819.                             self.device.id_dict = \
  1820.                                self.get_hpfax_device_id(faxuri)
  1821.                             self.device.uri = faxuri
  1822.                             self.auto_make = self.device.id_dict["MFG"]
  1823.                             self.auto_model = self.device.id_dict["MDL"]
  1824.                             self.device.id = "MFG:" + self.auto_make + \
  1825.                                              ";MDL:" + self.auto_model + \
  1826.                                              ";DES:" + \
  1827.                                              self.device.id_dict["DES"] + ";"
  1828.                 uri = self.device.uri
  1829.                 if uri and uri.startswith ("smb://"):
  1830.                     uri = SMBURI (uri=uri[6:]).sanitize_uri ()
  1831.  
  1832.                 # Try to access the PPD, in this case our detected IPP
  1833.                 # printer is a queue on a remote CUPS server which is
  1834.                 # not automatically set up on our local CUPS server
  1835.                 # (for example DNS-SD broadcasted queue from Mac OS X)
  1836.                 self.remotecupsqueue = None
  1837.                 res = re.search ("ipp://(\S+(:\d+|))/printers/(\S+)", uri)
  1838.                 if res:
  1839.                     resg = res.groups()
  1840.                     try:
  1841.                         conn = httplib.HTTPConnection(resg[0])
  1842.                         conn.request("GET", "/printers/%s.ppd" % resg[2])
  1843.                         resp = conn.getresponse()
  1844.                         if resp.status == 200: self.remotecupsqueue = resg[2]
  1845.                     except:
  1846.                         debugprint("exception in getting remotecupsqueue")
  1847.                         pass
  1848.  
  1849.                     # We also want to fetch the printer-info and
  1850.                     # printer-location attributes, to pre-fill those
  1851.                     # fields for this new queue.
  1852.                     oldserver = cups.getServer()
  1853.                     oldport = cups.getPort()
  1854.                     try:
  1855.                         cups.setServer (resg[0])
  1856.                         if len (resg[1]) > 0:
  1857.                             cups.setPort (int (resg[1]))
  1858.                         else:
  1859.                             cups.setPort (631)
  1860.  
  1861.                         c = cups.Connection ()
  1862.                         r = ['printer-info', 'printer-location']
  1863.                         attrs = c.getPrinterAttributes (uri=uri,
  1864.                                                         requested_attributes=r)
  1865.                         info = attrs.get ('printer-info', '')
  1866.                         location = attrs.get ('printer-location', '')
  1867.                         if len (info) > 0:
  1868.                             self.entNPDescription.setText(info)
  1869.                         if len (location) > 0:
  1870.                             self.entNPLocation.setText(location)
  1871.                     except:
  1872.                         nonfatalException ()
  1873.  
  1874.                     cups.setServer (oldserver)
  1875.                     cups.setPort (oldport)
  1876.  
  1877.                 if (not self.remotecupsqueue and
  1878.                     not self.new_printer_PPDs_loaded):
  1879.                     try:
  1880.                         self.loadPPDs(self)
  1881.                     except cups.IPPError, (e, msg):
  1882.                         #self.ready (self)
  1883.                         self.show_IPP_Error(e, msg)
  1884.                         return
  1885.                     except:
  1886.                         self.ready (self)
  1887.                         return
  1888.                     self.new_printer_PPDs_loaded = True
  1889.  
  1890.                 ppdname = None
  1891.                 try:
  1892.                     if self.remotecupsqueue:
  1893.                         # We have a remote CUPS queue, let the client queue
  1894.                         # stay raw so that the driver on the server gets used
  1895.                         ppdname = 'raw'
  1896.                         self.ppd = ppdname
  1897.                         name = self.remotecupsqueue
  1898.                         name = self.mainapp.makeNameUnique (name)
  1899.                         self.entNPName.setText(name)
  1900.                     elif self.device.id:
  1901.                         id_dict = self.device.id_dict
  1902.                         (status, ppdname) = self.ppds.\
  1903.                             getPPDNameFromDeviceID (id_dict["MFG"],
  1904.                                                     id_dict["MDL"],
  1905.                                                     id_dict["DES"],
  1906.                                                     id_dict["CMD"],
  1907.                                                     self.device.uri)
  1908.                     else:
  1909.                         (status, ppdname) = self.ppds.\
  1910.                             getPPDNameFromDeviceID ("Generic",
  1911.                                                     "Printer",
  1912.                                                     "Generic Printer",
  1913.                                                     [],
  1914.                                                     self.device.uri)
  1915.  
  1916.                     if ppdname and not self.remotecupsqueue:
  1917.                         ppddict = self.ppds.getInfoFromPPDName (ppdname)
  1918.                         make_model = ppddict['ppd-make-and-model']
  1919.                         (make, model) = \
  1920.                             cupshelpers.ppds.ppdMakeModelSplit (make_model)
  1921.                         self.auto_make = make
  1922.                         self.auto_model = model
  1923.                 except:
  1924.                     nonfatalException ()
  1925.                 if not self.remotecupsqueue:
  1926.                     self.fillMakeList()
  1927.             elif page_nr == 3: # Model has been selected
  1928.                 if not self.device.id:
  1929.                     # Choose an appropriate name when no Device ID
  1930.                     # is available, based on the model the user has
  1931.                     # selected.
  1932.                     try:
  1933.                         items = self.tvNPModels.selectedItems()
  1934.                         name = unicode(items[0].text())
  1935.                         name = self.mainapp.makeNameUnique (name)
  1936.                         self.entNPName.setText(name)
  1937.                     except:
  1938.                         nonfatalException ()
  1939.  
  1940.             ##self.ready (self.NewPrinterWindow)
  1941.             if self.remotecupsqueue:
  1942.                 order = [1, 0]
  1943.             elif self.rbtnNPFoomatic.isChecked():
  1944.                 order = [1, 2, 3, 6, 0]
  1945.             elif self.rbtnNPPPD.isChecked():
  1946.                 order = [1, 2, 6, 0]
  1947.             else:
  1948.                 # Downloadable driver
  1949.                 order = [1, 2, 7, 6, 0]
  1950.         elif self.dialog_mode == "device":
  1951.             order = [1]
  1952.         elif self.dialog_mode == "ppd":
  1953.             self.rbtnChangePPDasIs.setChecked(True)
  1954.             if self.rbtnNPFoomatic.isChecked():
  1955.                 order = [2, 3, 5, 6]
  1956.             elif self.rbtnNPPPD.isChecked():
  1957.                 order = [2, 5, 6]
  1958.             else:
  1959.                 # Downloadable driver
  1960.                 order = [2, 7, 5, 6]
  1961.  
  1962.         next_page_nr = order[order.index(page_nr)+step]
  1963.  
  1964.         # fill Installable Options tab
  1965.         if next_page_nr == 6 and step > 0:
  1966.             #TODO Prepare Installable Options screen.
  1967.             self.ppd = self.getNPPPD()
  1968.             """FIXME todo
  1969.             if next_page_nr == 6:
  1970.                 # Prepare Installable Options screen.
  1971.                 if isinstance(self.ppd, cups.PPD):
  1972.                     self.fillNPInstallableOptions()
  1973.                 else:
  1974.                     self.installable_options = None
  1975.                     # Put a label there explaining why the page is empty.
  1976.                     ppd = self.ppd
  1977.                     self.ppd = None
  1978.                     self.fillNPInstallableOptions()
  1979.                     self.ppd = ppd
  1980.  
  1981.                 # step over if empty and not in PPD mode
  1982.                 if self.dialog_mode != "ppd" and not self.installable_options:
  1983.                     next_page_nr = order[order.index(next_page_nr)+1]
  1984.             """
  1985.             next_page_nr = order[order.index(next_page_nr)+1]
  1986.         self.installable_options = False
  1987.         # Step over empty Installable Options tab
  1988.         if next_page_nr == 6 and not self.installable_options and step<0:
  1989.             next_page_nr = order[order.index(next_page_nr)-1]
  1990.  
  1991.         if next_page_nr == 7: # About to show downloadable drivers
  1992.             if self.drivers_lock.locked ():
  1993.                 # Still searching for drivers.
  1994.                 self.lblWait.set_markup ('<span weight="bold" size="larger">' +
  1995.                                          _('Searching') + '</span>\n\n' +
  1996.                                          _('Searching for drivers'))
  1997.                 self.WaitWindow.set_transient_for (self.NewPrinterWindow)
  1998.                 self.WaitWindow.show ()
  1999.                 self.busy(self)
  2000.  
  2001.                 # Keep the UI refreshed while we wait for the drivers
  2002.                 # query to complete.
  2003.                 while self.drivers_lock.locked ():
  2004.                     while gtk.events_pending ():
  2005.                         gtk.main_iteration ()
  2006.                     time.sleep (0.1)
  2007.  
  2008.                 self.ready (self.NewPrinterWindow)
  2009.                 self.WaitWindow.hide ()
  2010.  
  2011.             self.fillDownloadableDrivers()
  2012.  
  2013.         self.ntbkNewPrinter.setCurrentIndex(next_page_nr)
  2014.  
  2015.         self.setNPButtons()
  2016.  
  2017.     def setNPButtons(self):
  2018.         nr = self.ntbkNewPrinter.currentIndex()
  2019.  
  2020.         if self.dialog_mode == "device":
  2021.             self.btnNPBack.hide()
  2022.             self.btnNPForward.hide()
  2023.             self.btnNPApply.show()
  2024.             uri = self.getDeviceURI ()
  2025.             self.btnNPApply.setEnabled(validDeviceURI (uri))
  2026.             return
  2027.  
  2028.         if self.dialog_mode == "ppd":
  2029.             if nr == 5: # Apply
  2030.                 if not self.installable_options:
  2031.                     # There are no installable options, so this is the
  2032.                     # last page.
  2033.                     debugprint ("No installable options")
  2034.                     self.btnNPForward.hide ()
  2035.                     self.btnNPApply.show ()
  2036.                 else:
  2037.                     self.btnNPForward.show ()
  2038.                     self.btnNPApply.hide ()
  2039.                 return
  2040.             elif nr == 6:
  2041.                 self.btnNPForward.hide()
  2042.                 self.btnNPApply.show()
  2043.                 return
  2044.             else:
  2045.                 self.btnNPForward.show()
  2046.                 self.btnNPApply.hide()
  2047.             if nr == 2:
  2048.                 self.btnNPBack.hide()
  2049.                 self.btnNPForward.show()
  2050.                 downloadable_selected = False
  2051.                 if self.rbtnNPDownloadableDriverSearch.isChecked():
  2052.                     combobox = self.cmbNPDownloadableDriverFoundPrinters
  2053.                     iter = combobox.get_active_iter () #FIXME
  2054.                     if iter and combobox.get_model ().get_value (iter, 1):
  2055.                         downloadable_selected = True
  2056.  
  2057.                 self.btnNPForward.setEnabled(bool(
  2058.                         self.rbtnNPFoomatic.isChecked() or
  2059.                         self.filechooserPPD.get_filename() or
  2060.                         downloadable_selected))
  2061.                 return
  2062.             else:
  2063.                 self.btnNPBack.show()
  2064.  
  2065.         # class/printer
  2066.  
  2067.         if nr == 1: # Device
  2068.             valid = False
  2069.             try:
  2070.                 uri = self.getDeviceURI ()
  2071.                 valid = validDeviceURI (uri)
  2072.             except:
  2073.                 pass
  2074.             self.btnNPForward.setEnabled(valid)
  2075.             self.btnNPBack.hide ()
  2076.         else:
  2077.             self.btnNPBack.show()
  2078.  
  2079.         self.btnNPForward.show()
  2080.         self.btnNPApply.hide()
  2081.  
  2082.         if nr == 0: # Name
  2083.             self.btnNPBack.show()
  2084.             if self.dialog_mode == "class":
  2085.                 self.btnNPForward.setEnabled(True)
  2086.             if self.dialog_mode == "printer":
  2087.                 self.btnNPForward.hide()
  2088.                 self.btnNPApply.show()
  2089.                 self.btnNPApply.setEnabled(
  2090.                     self.mainapp.checkNPName(unicode(self.entNPName.text())))
  2091.         if nr == 2: # Make/PPD file
  2092.             downloadable_selected = False
  2093.             if self.rbtnNPDownloadableDriverSearch.isChecked():
  2094.                 combobox = self.cmbNPDownloadableDriverFoundPrinters
  2095.                 iter = combobox.get_active_iter () #FIXME
  2096.                 if iter and combobox.get_model ().get_value (iter, 1):
  2097.                     downloadable_selected = True
  2098.  
  2099.             self.btnNPForward.setEnabled(bool(
  2100.                 self.rbtnNPFoomatic.isChecked() or
  2101.                 self.filechooserPPD.get_filename() or
  2102.                 downloadable_selected))
  2103.         if nr == 3: # Model/Driver
  2104.             iter = self.tvNPDrivers.currentItem()
  2105.             self.btnNPForward.setEnabled(bool(iter))
  2106.         if nr == 4: # Class Members
  2107.             self.btnNPForward.hide()
  2108.             self.btnNPApply.show()
  2109.             self.btnNPApply.setEnabled(self.tvNCMembers.count() > 0)
  2110.         if nr == 7: # Downloadable drivers
  2111.             if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1: #FIXME
  2112.                 accepted = self.rbtnNPDownloadLicenseYes.get_active ()
  2113.             else:
  2114.                 treeview = self.tvNPDownloadableDrivers
  2115.                 model, iter = treeview.get_selection ().get_selected ()
  2116.                 accepted = (iter != None)
  2117.  
  2118.             self.btnNPForward.set_sensitive(accepted)
  2119.  
  2120.     def getDevices_thread(self):
  2121.         try:
  2122.             debugprint ("Connecting (devices)")
  2123.             cups.setServer (self.mainapp.connect_server)
  2124.             #cups.setUser (self.mainapp.connect_user)
  2125.             cups.setUser ("jr")
  2126.             cups.setPasswordCB (self.mainapp.cupsPasswdCallback)
  2127.             # cups.setEncryption (...)
  2128.             c = cups.Connection ()
  2129.             debugprint ("Fetching devices")
  2130.             self.devices_result = cupshelpers.getDevices(c)
  2131.         except cups.IPPError, (e, msg):
  2132.             self.devices_result = cups.IPPError (e, msg)
  2133.         except:
  2134.             debugprint ("Exception in getDevices_thread")
  2135.             self.devices_result = {}
  2136.  
  2137.         try:
  2138.             debugprint ("Closing connection (devices)")
  2139.             del c
  2140.         except:
  2141.             pass
  2142.  
  2143.         debugprint ("Releasing devices lock")
  2144.         self.devices_lock.release ()
  2145.  
  2146.     # Device URI
  2147.     def queryDevices(self):
  2148.         if not self.devices_lock.acquire(0):
  2149.             debugprint ("queryDevices: in progress")
  2150.             return
  2151.         debugprint ("Lock acquired for devices thread")
  2152.         # Start new thread
  2153.         thread.start_new_thread (self.getDevices_thread, ())
  2154.         #self.getDevices_thread()
  2155.         debugprint ("Devices thread started")
  2156.  
  2157.     def fetchDevices(self, parent=None):
  2158.         debugprint ("fetchDevices")
  2159.         self.queryDevices ()
  2160.         time.sleep (0.1)
  2161.  
  2162.         # Keep the UI refreshed while we wait for the devices to load.
  2163.         waiting = False
  2164.         while (self.devices_lock.locked()):
  2165.             if not waiting:
  2166.                 waiting = True
  2167.                 self.WaitWindow.setText ('<b>' +
  2168.                                          _('Searching') + '</b><br/><br/>' +
  2169.                                          _('Searching for printers'))
  2170.                 #if not parent:
  2171.                 #    parent = self.mainapp.MainWindow
  2172.                 #self.WaitWindow.set_transient_for (parent)
  2173.                 #self.WaitWindow.show ()
  2174.                 self.WaitWindow.show()
  2175.  
  2176.             QApplication.processEvents()
  2177.  
  2178.             time.sleep (0.1)
  2179.  
  2180.         if waiting:
  2181.             #self.WaitWindow.hide ()
  2182.             self.WaitWindow.hide()
  2183.  
  2184.         debugprint ("Got devices")
  2185.         result = self.devices_result # atomic operation
  2186.         if isinstance (result, cups.IPPError):
  2187.             # Propagate exception.
  2188.             raise result
  2189.         return result
  2190.  
  2191.     def get_hpfax_device_id(self, faxuri):
  2192.         os.environ["URI"] = faxuri
  2193.         cmd = 'LC_ALL=C DISPLAY= hp-info -d "${URI}"'
  2194.         debugprint (faxuri + ": " + cmd)
  2195.         try:
  2196.             p = subprocess.Popen (cmd, shell=True,
  2197.                                   stdin=file("/dev/null"),
  2198.                                   stdout=subprocess.PIPE,
  2199.                                   stderr=subprocess.PIPE)
  2200.             (stdout, stderr) = p.communicate ()
  2201.         except:
  2202.             # Problem executing command.
  2203.             return None
  2204.  
  2205.         for line in stdout.split ("\n"):
  2206.             if line.find ("fax-type") == -1:
  2207.                 continue
  2208.             faxtype = -1
  2209.             res = re.search ("(\d+)", line)
  2210.             if res:
  2211.                 resg = res.groups()
  2212.                 faxtype = resg[0]
  2213.             if faxtype >= 0: break
  2214.         if faxtype < 0:
  2215.             return None
  2216.         elif faxtype == 4:
  2217.             return cupshelpers.parseDeviceID ('MFG:HP;MDL:Fax 2;DES:HP Fax 2;')
  2218.         else:
  2219.             return cupshelpers.parseDeviceID ('MFG:HP;MDL:Fax;DES:HP Fax;')
  2220.  
  2221.     def get_hplip_uri_for_network_printer(self, host, mode):
  2222.         os.environ["HOST"] = host
  2223.         if mode == "print": mod = "-c"
  2224.         elif mode == "fax": mod = "-f"
  2225.         else: mod = "-c"
  2226.         cmd = 'hp-makeuri ' + mod + ' "${HOST}"'
  2227.         debugprint (host + ": " + cmd)
  2228.         uri = None
  2229.         try:
  2230.             p = subprocess.Popen (cmd, shell=True,
  2231.                                   stdin=file("/dev/null"),
  2232.                                   stdout=subprocess.PIPE,
  2233.                                   stderr=subprocess.PIPE)
  2234.             (stdout, stderr) = p.communicate ()
  2235.         except:
  2236.             # Problem executing command.
  2237.             return None
  2238.  
  2239.         uri = stdout.strip ()
  2240.         return uri
  2241.  
  2242.     def getNetworkPrinterMakeModel(self, device):
  2243.         # Determine host name/IP
  2244.         host = None
  2245.         s = device.uri.find ("://")
  2246.         if s != -1:
  2247.             s += 3
  2248.             e = device.uri[s:].find (":")
  2249.             if e == -1: e = device.uri[s:].find ("/")
  2250.             if e == -1: e = device.uri[s:].find ("?")
  2251.             if e == -1: e = len (device.uri)
  2252.             host = device.uri[s:s+e]
  2253.         # Try to get make and model via SNMP
  2254.         if host:
  2255.             os.environ["HOST"] = host
  2256.             cmd = '/usr/lib/cups/backend/snmp "${HOST}"'
  2257.             debugprint (host + ": " + cmd)
  2258.             stdout = None
  2259.             try:
  2260.                 p = subprocess.Popen (cmd, shell=True,
  2261.                                       stdin=file("/dev/null"),
  2262.                                       stdout=subprocess.PIPE,
  2263.                                       stderr=subprocess.PIPE)
  2264.                 (stdout, stderr) = p.communicate ()
  2265.             except:
  2266.                 # Problem executing command.
  2267.                 pass
  2268.  
  2269.             if stdout != None:
  2270.                 mm = re.sub("^\s*\S+\s+\S+\s+\"", "", stdout)
  2271.                 mm = re.sub("\"\s+.*$", "", mm)
  2272.                 if mm and mm != "": device.make_and_model = mm
  2273.         # Extract make and model and create a pseudo device ID, so
  2274.         # that a PPD/driver can be assigned to the device
  2275.         make_and_model = None
  2276.         if len (device.make_and_model) > 7:
  2277.             make_and_model = device.make_and_model
  2278.         elif len (device.info) > 7:
  2279.             make_and_model = device.info
  2280.             make_and_model = re.sub("\s*(\(|\d+\.\d+\.\d+\.\d+).*$", "", make_and_model)
  2281.         if make_and_model and not device.id:
  2282.             mk = None
  2283.             md = None
  2284.             (mk, md) = cupshelpers.ppds.ppdMakeModelSplit (make_and_model)
  2285.             device.id = "MFG:" + mk + ";MDL:" + md + ";DES:" + mk + " " + md + ";"
  2286.             device.id_dict = cupshelpers.parseDeviceID (device.id)
  2287.         # Check whether the device is supported by HPLIP and replace
  2288.         # its URI by an HPLIP URI
  2289.         if host:
  2290.             hplipuri = self.get_hplip_uri_for_network_printer(host, "print")
  2291.             if hplipuri:
  2292.                 device.uri = hplipuri
  2293.                 s = hplipuri.find ("/usb/")
  2294.                 if s == -1: s = hplipuri.find ("/par/")
  2295.                 if s == -1: s = hplipuri.find ("/net/")
  2296.                 if s != -1:
  2297.                     s += 5
  2298.                     e = hplipuri[s:].find ("?")
  2299.                     if e == -1: e = len (hplipuri)
  2300.                     mdl = hplipuri[s:s+e].replace ("_", " ")
  2301.                     if mdl.startswith ("hp ") or mdl.startswith ("HP "):
  2302.                         mdl = mdl[3:]
  2303.                         device.make_and_model = "HP " + mdl
  2304.                         device.id = "MFG:HP;MDL:" + mdl + ";DES:HP " + mdl + ";"
  2305.                         device.id_dict = cupshelpers.parseDeviceID (device.id)
  2306.         # Return the host name/IP for further actions
  2307.         return host
  2308.  
  2309.     def fillDeviceTab(self, current_uri=None, query=True):
  2310.         if query:
  2311.             try:
  2312.                 devices = self.fetchDevices()
  2313.             except cups.IPPError, (e, msg):
  2314.                 self.show_IPP_Error(e, msg)
  2315.                 devices = {}
  2316.             except:
  2317.                 nonfatalException()
  2318.                 devices = {}
  2319.  
  2320.             if current_uri:
  2321.                 if devices.has_key (current_uri):
  2322.                     current = devices.pop(current_uri)
  2323.                 else:
  2324.                     current = cupshelpers.Device (current_uri)
  2325.                     current.info = "Current device"
  2326.  
  2327.             self.devices = devices.values()
  2328.  
  2329.         for device in self.devices:
  2330.             if device.type == "usb":
  2331.                 # Find USB URIs with corresponding HPLIP URIs and mark them
  2332.                 # for deleting, so that the user will only get the HPLIP
  2333.                 # URIs for full device support in the list
  2334.                 ser = None
  2335.                 s = device.uri.find ("?serial=")
  2336.                 if s != -1:
  2337.                     s += 8
  2338.                     e = device.uri[s:].find ("?")
  2339.                     if e == -1: e = len (device.uri)
  2340.                     ser = device.uri[s:s+e]
  2341.                 mod = None
  2342.                 s = device.uri[6:].find ("/")
  2343.                 if s != -1:
  2344.                     s += 7
  2345.                     e = device.uri[s:].find ("?")
  2346.                     if e == -1: e = len (device.uri)
  2347.                     mod = device.uri[s:s+e].lower ().replace ("%20", "_")
  2348.                     if mod.startswith ("hp_"):
  2349.                         mod = mod[3:]
  2350.                 matchfound = 0
  2351.                 for hpdevice in self.devices:
  2352.                     hpser = None
  2353.                     hpmod = None
  2354.                     uri = hpdevice.uri
  2355.                     if not uri.startswith ("hp:"): continue
  2356.                     if ser:
  2357.                         s = uri.find ("?serial=")
  2358.                         if s != -1:
  2359.                             s += 8
  2360.                             e = uri[s:].find ("?")
  2361.                             if e == -1: e = len (uri)
  2362.                             hpser = uri[s:s+e]
  2363.                             if hpser != ser: continue
  2364.                             matchfound = 1
  2365.                     if mod and not (ser and hpser):
  2366.                         s = uri.find ("/usb/")
  2367.                         if s != -1:
  2368.                             s += 5
  2369.                             e = uri[s:].find ("?")
  2370.                             if e == -1: e = len (uri)
  2371.                             hpmod = uri[s:s+e].lower ()
  2372.                             if hpmod.startswith ("hp_"):
  2373.                                 hpmod = hpmod[3:]
  2374.                             if hpmod != mod: continue
  2375.                             matchfound = 1
  2376.                     if matchfound == 1: break
  2377.                 if matchfound == 1:
  2378.                     device.uri = "delete"
  2379.             if device.type == "hal":
  2380.                 # Remove HAL USB URIs, for these printers there are already
  2381.                 # USB URIs
  2382.                 if device.uri.startswith("hal:///org/freedesktop/Hal/devices/usb_device"):
  2383.                     device.uri = "delete"
  2384.             if device.type == "socket":
  2385.                 # Remove default port to more easily find duplicate URIs
  2386.                 device.uri = device.uri.replace (":9100", "")
  2387.             try:
  2388.                 ## XXX This needs to be moved to *after* the device is
  2389.                 # selected.  Looping through all the network printers like
  2390.                 # this is far too slow.
  2391.                 if False and device.type in ("socket", "lpd", "ipp", "bluetooth"):
  2392.                     host = self.getNetworkPrinterMakeModel(device)
  2393.                     faxuri = None
  2394.                     if host:
  2395.                         faxuri = self.get_hplip_uri_for_network_printer(host,
  2396.                                                                         "fax")
  2397.                     if faxuri:
  2398.                         self.devices.append(cupshelpers.Device(faxuri,
  2399.                               **{'device-class' : "direct",
  2400.                                  'device-info' : device.info + " HP Fax HPLIP",
  2401.                                  'device-device-make-and-model' : "HP Fax",
  2402.                                  'device-id' : "MFG:HP;MDL:Fax;DES:HP Fax;"}))
  2403.                     if device.uri.startswith ("hp:"):
  2404.                         device.type = "hp" 
  2405.                         device.info += (" HPLIP")
  2406.             except:
  2407.                 nonfatalException ()
  2408.         # Mark duplicate URIs for deletion
  2409.         for i in range (len (self.devices)):
  2410.             for j in range (len (self.devices)):
  2411.                 if i == j: continue
  2412.                 device1 = self.devices[i]
  2413.                 device2 = self.devices[j]
  2414.                 if device1.uri == "delete" or device2.uri == "delete":
  2415.                     continue
  2416.                 if device1.uri == device2.uri:
  2417.                     # Keep the one with the longer (better) device ID
  2418.                     if (not device1.id):
  2419.                         device1.uri = "delete"
  2420.                     elif (not device2.id):
  2421.                         device2.uri = "delete"
  2422.                     elif (len (device1.id) < len (device2.id)):
  2423.                         device1.uri = "delete"
  2424.                     else:
  2425.                         device2.uri = "delete"
  2426.         self.devices = filter(lambda x: x.uri not in ("hp:/no_device_found",
  2427.                                                       "hpfax:/no_device_found",
  2428.                                                       "hp", "hpfax",
  2429.                                                       "hal", "beh",
  2430.                                                       "scsi", "http", "delete"),
  2431.                               self.devices)
  2432.         self.devices.sort()
  2433.  
  2434.         self.devices.append(cupshelpers.Device('',
  2435.              **{'device-info' :_("Other")}))
  2436.         if current_uri:
  2437.             current.info += _(" (Current)")
  2438.             self.devices.insert(0, current)
  2439.             self.device = current
  2440.         self.tvNPDevices.clear()
  2441.  
  2442.         for device in self.devices:
  2443.             self.tvNPDevices.addItem(device.info)
  2444.  
  2445.         #self.tvNPDevices.get_selection().select_path(0)
  2446.         self.tvNPDevices.setCurrentRow(0)
  2447.         self.on_tvNPDevices_cursor_changed()
  2448.  
  2449.     def on_entNPTDevice_changed(self, entry):
  2450.         self.setNPButtons()
  2451.  
  2452.     #TODO
  2453.     ## SMB browsing
  2454.     def on_entSMBURI_changed (self, text):
  2455.         uri = unicode(text)
  2456.         (group, host, share, user, password) = SMBURI (uri=uri).separate ()
  2457.         if user:
  2458.             self.entSMBUsername.setText(user)
  2459.         if password:
  2460.             self.entSMBPassword.setText(password)
  2461.         if user or password:
  2462.             uri = SMBURI (group=group, host=host, share=share).get_uri ()
  2463.             self.entSMBURI.setText(uri)
  2464.             self.rbtnSMBAuthSet.setChecked(True)
  2465.         elif unicode(self.entSMBUsername.text()) == '':
  2466.             self.rbtnSMBAuthPrompt.setChecked(True)
  2467.  
  2468.         self.btnSMBVerify.setEnabled(bool(uri))
  2469.  
  2470.     def on_rbtnSMBAuthSet_toggled(self, ticked):
  2471.         self.tblSMBAuth.setEnabled(ticked)
  2472.  
  2473.     def on_entNPTIPPHostname_textChanged(self):
  2474.         valid = len (self.entNPTIPPHostname.text ()) > 0
  2475.         self.btnIPPFindQueue.setEnabled(valid)
  2476.         self.update_IPP_URI_label ()
  2477.  
  2478.     ### IPP Browsing
  2479.     def update_IPP_URI_label(self):
  2480.         hostname = unicode(self.entNPTIPPHostname.text())
  2481.         queue = unicode(self.entNPTIPPQueuename.text())
  2482.         valid = len (hostname) > 0 and queue != '/printers/'
  2483.  
  2484.         if valid:
  2485.             uri = "ipp://%s%s" % (hostname, queue)
  2486.             self.lblIPPURI.setText(uri)
  2487.             self.lblIPPURI.show ()
  2488.             self.entNPTIPPQueuename.show ()
  2489.         else:
  2490.             self.lblIPPURI.hide ()
  2491.  
  2492.         self.btnIPPVerify.setEnabled(valid)
  2493.         self.setNPButtons ()
  2494.  
  2495.     #FIXME this seems totally different from the Gnome one
  2496.     @pyqtSignature("")
  2497.     def on_btnIPPFindQueue_clicked(self):
  2498.         self.IPPBrowseBox.clear()
  2499.         host = str(self.entNPTIPPHostname.text())
  2500.         cups.setServer (host)
  2501.         printers = classes = {}
  2502.         try:
  2503.             c = cups.Connection()
  2504.             printers = c.getPrinters ()
  2505.             classes = c.getClasses ()
  2506.             del c
  2507.         except RuntimeError:
  2508.             pass
  2509.         except cups.IPP_Error, (e, msg):
  2510.             pass
  2511.  
  2512.         for printer, dict in printers.iteritems ():
  2513.  
  2514.             self.IPPBrowseBox.addItem(printer)
  2515. #            store.set_value (iter, 0, printer)
  2516. #            store.set_value (iter, 1, dict.get ('printer-location', ''))
  2517. #            store.set_value (iter, 2, dict)
  2518.         for pclass, dict in classes.iteritems ():
  2519.             pass
  2520. #            iter = store.append (None)
  2521. #            store.set_value (iter, 0, pclass)
  2522. #            store.set_value (iter, 1, dict.get ('printer-location', ''))
  2523. #            store.set_value (iter, 2, dict)
  2524.  
  2525.         if len (printers) + len (classes) == 0:
  2526.             # Display 'No queues' dialog
  2527.             QMessageBox.information(self, _("No queues"),_("There are no queues available."))
  2528.  
  2529.     def on_tvNPDevices_cursor_changed(self):
  2530.         device = self.devices[self.tvNPDevices.currentRow()]
  2531.         self.device = device
  2532.         self.lblNPDeviceDescription.setText('')
  2533.         page = self.new_printer_device_tabs.get(device.type, 1)
  2534.         self.ntbkNPType.setCurrentIndex(page)
  2535.  
  2536.         type = device.type
  2537.         url = device.uri.split(":", 1)[-1]
  2538.         if page == 0:
  2539.             # This is the "no options" page, with just a label to describe
  2540.             # the selected device.
  2541.             if device.type == "parallel":
  2542.                 text = _("A printer connected to the parallel port.")
  2543.             elif device.type == "usb":
  2544.                 text = _("A printer connected to a USB port.")
  2545.             elif device.type == "hp":
  2546.                 text = _("HPLIP software driving a printer, "
  2547.                          "or the printer function of a multi-function device.")
  2548.             elif device.type == "hpfax":
  2549.                 text = _("HPLIP software driving a fax machine, "
  2550.                          "or the fax function of a multi-function device.")
  2551.             elif device.type == "hal":
  2552.                 text = _("Local printer detected by the "
  2553.                          "Hardware Abstraction Layer (HAL).")
  2554.             else:
  2555.                 text = device.uri
  2556.  
  2557.             self.lblNPDeviceDescription.setText(text)
  2558.         elif device.type=="socket":
  2559.             if device.uri.startswith ("socket"):
  2560.                 host = device.uri[9:]
  2561.                 i = host.find (":")
  2562.                 if i != -1:
  2563.                     port = int (host[i + 1:])
  2564.                     host = host[:i]
  2565.                 else:
  2566.                     port = 9100
  2567.  
  2568.                 self.entNPTDirectJetHostname.setText(host)
  2569.                 self.entNPTDirectJetPort.setText(str (port))
  2570.         elif device.type=="serial":
  2571.             if not device.is_class:
  2572.                 options = device.uri.split("?")[1]
  2573.                 options = options.split("+")
  2574.                 option_dict = {}
  2575.                 for option in options:
  2576.                     name, value = option.split("=")
  2577.                     option_dict[name] = value
  2578.                     
  2579.                 for widget, name, optionvalues in (
  2580.                     (self.cmbNPTSerialBaud, "baud", None),
  2581.                     (self.cmbNPTSerialBits, "bits", None),
  2582.                     (self.cmbNPTSerialParity, "parity",
  2583.                      ["none", "odd", "even"]),
  2584.                     (self.cmbNPTSerialFlow, "flow",
  2585.                      ["none", "soft", "hard", "hard"])):
  2586.                     if option_dict.has_key(name): # option given in URI?
  2587.                         if optionvalues is None: # use text in widget
  2588.                             model = widget.get_model()
  2589.                             iter = model.get_iter_first()
  2590.                             nr = 0
  2591.                             while iter:
  2592.                                 value = model.get(iter,0)[0]
  2593.                                 if value == option_dict[name]:
  2594.                                     widget.set_active(nr)
  2595.                                     break
  2596.                                 iter = model.iter_next(iter)
  2597.                                 nr += 1
  2598.                         else: # use optionvalues
  2599.                             nr = optionvalues.index(
  2600.                                 option_dict[name])
  2601.                             widget.set_active(nr+1) # compensate "Default"
  2602.                     else:
  2603.                         widget.set_active(0)
  2604.  
  2605.         # XXX FILL TABS FOR VALID DEVICE URIs
  2606.         elif device.type in ("ipp", "http"):
  2607.             if (device.uri.startswith ("ipp:") or
  2608.                 device.uri.startswith ("http:")):
  2609.                 match = re.match ("(ipp|https?)://([^/]+)(.*)", device.uri)
  2610.                 if match:
  2611.                     server = match.group (2)
  2612.                     printer = match.group (3)
  2613.                 else:
  2614.                     server = ""
  2615.                     printer = ""
  2616.  
  2617.                 self.entNPTIPPHostname.setText(server)
  2618.                 self.entNPTIPPQueuename.setText(printer)
  2619.                 self.lblIPPURI.setText(device.uri)
  2620.                 self.lblIPPURI.show()
  2621.                 self.entNPTIPPQueuename.show()
  2622.             else:
  2623.                 self.entNPTIPPHostname.setText('')
  2624.                 self.entNPTIPPQueuename.setText('/printers/')
  2625.                 self.entNPTIPPQueuename.show()
  2626.                 self.lblIPPURI.hide()
  2627.         elif device.type=="lpd":
  2628.             if device.uri.startswith ("lpd"):
  2629.                 host = device.uri[6:]
  2630.                 i = host.find ("/")
  2631.                 if i != -1:
  2632.                     printer = host[i + 1:]
  2633.                     host = host[:i]
  2634.                 else:
  2635.                     printer = ""
  2636.                 self.cmbentNPTLpdHost.addItem(host)
  2637.                 self.cmbentNPTLpdQueue.addItem(printer)
  2638.         elif device.uri == "lpd":
  2639.             pass
  2640.         elif device.uri == "smb":
  2641.             self.entSMBURI.setText('')
  2642.             self.btnSMBVerify.setEnabled(False)
  2643.         elif device.type == "smb":
  2644.             self.entSMBUsername.setText('')
  2645.             self.entSMBPassword.setText('')
  2646.             self.entSMBURI.set_text(device.uri[6:])
  2647.             self.btnSMBVerify.setEnabled(True)
  2648.         else:
  2649.             self.entNPTDevice.setText(device.uri)
  2650.  
  2651.         self.setNPButtons()
  2652.  
  2653.     def getDeviceURI(self):
  2654.         type = self.device.type
  2655.         if type == "socket": # DirectJet
  2656.             host = unicode(self.entNPTDirectJetHostname.text())
  2657.             port = unicode(self.entNPTDirectJetPort.text())
  2658.             device = "socket://" + host
  2659.             if port:
  2660.                 device = device + ':' + port
  2661.         elif type in ("http", "ipp"): # IPP
  2662.             if self.lblIPPURI.isVisible:
  2663.                 device = unicode(self.lblIPPURI.text())
  2664.             else:
  2665.                 device = "ipp"
  2666.         elif type == "lpd": # LPD
  2667.             host = unicode(self.cmbentNPTLpdHost.currentText())
  2668.             printer = unicode(self.cmbentNPTLpdQueue.currentText())
  2669.             device = "lpd://" + host
  2670.             if printer:
  2671.                 device = device + "/" + printer
  2672.         elif type == "parallel": # Parallel
  2673.             device = self.device.uri
  2674.         elif type == "scsi": # SCSII
  2675.             device = ""
  2676.         elif type == "serial": # Serial
  2677.             options = []
  2678.             for widget, name, optionvalues in (
  2679.                 (self.cmbNPTSerialBaud, "baud", None),
  2680.                 (self.cmbNPTSerialBits, "bits", None),
  2681.                 (self.cmbNPTSerialParity, "parity",
  2682.                  ("none", "odd", "even")),
  2683.                 (self.cmbNPTSerialFlow, "flow",
  2684.                  ("none", "soft", "hard", "hard"))):
  2685.                 nr = widget.get_active()
  2686.                 if nr:
  2687.                     if optionvalues is not None:
  2688.                         option = optionvalues[nr-1]
  2689.                     else:
  2690.                         option = widget.get_active_text()
  2691.                     options.append(name + "=" + option)
  2692.             options = "+".join(options)
  2693.             device =  self.device.uri.split("?")[0] #"serial:/dev/ttyS%s" 
  2694.             if options:
  2695.                 device = device + "?" + options
  2696.         elif type == "smb":
  2697.             uri = unicode(self.entSMBURI.text())
  2698.             (group, host, share, u, p) = SMBURI (uri=uri).separate ()
  2699.             user = ''
  2700.             password = ''
  2701.             if self.rbtnSMBAuthSet.isChecked():
  2702.                 user = unicode(self.entSMBUsername.text())
  2703.                 password = unicode(self.entSMBPassword.text())
  2704.             uri = SMBURI (group=group, host=host, share=share,
  2705.                           user=user, password=password).get_uri ()
  2706.             device = "smb://" + uri
  2707.         elif not self.device.is_class:
  2708.             device = self.device.uri
  2709.         else:
  2710.             device = str(self.entNPTDevice.text())
  2711.         return device
  2712.         # class/printer
  2713.  
  2714.         if nr == self.ntbkNewPrinterPages["device"]: # Device
  2715.             valid = False
  2716.             try:
  2717.                 uri = self.getDeviceURI ()
  2718.                 valid = validDeviceURI (uri)
  2719.             except:
  2720.                 debugprint("exception in getDeviceURI()")
  2721.                 pass
  2722.             self.btnNPForward.setEnabled(valid)
  2723.             self.btnNPBack.hide ()
  2724.         else:
  2725.             self.btnNPBack.show()
  2726.  
  2727.         self.btnNPForward.show()
  2728.         self.btnNPApply.hide()
  2729.  
  2730.         if nr == self.ntbkNewPrinterPages["name"]: # Name
  2731.             self.btnNPBack.show()
  2732.             if self.dialog_mode == "printer":
  2733.                 self.btnNPForward.hide()
  2734.                 self.btnNPApply.show()
  2735.                 self.btnNPApply.setEnabled(
  2736.                     self.mainapp.checkNPName(self.entNPName.getText()))
  2737.         if nr == self.ntbkNewPrinterPages["make"]: # Make/PPD file
  2738.             downloadable_selected = False
  2739.             if self.rbtnNPDownloadableDriverSearch.get_active ():
  2740.                 combobox = self.cmbNPDownloadableDriverFoundPrinters
  2741.                 iter = combobox.get_active_iter ()
  2742.                 if iter and combobox.get_model ().get_value (iter, 1):
  2743.                     downloadable_selected = True
  2744.  
  2745.             self.btnNPForward.setEnabled(bool(
  2746.                 self.rbtnNPFoomatic.get_active() or
  2747.                 self.filechooserPPD.get_filename() or
  2748.                 downloadable_selected))
  2749.         if nr == self.ntbkNewPrinterPages["model"]: # Model/Driver
  2750.             model, iter = self.tvNPDrivers.get_selection().get_selected()
  2751.             self.btnNPForward.set_sensitive(bool(iter))
  2752.         if nr == self.ntbkNewPrinterPages["class-members"]: # Class Members
  2753.             self.btnNPForward.hide()
  2754.             self.btnNPApply.show()
  2755.             self.btnNPApply.setEnabled(
  2756.                 bool(self.mainapp.getCurrentClassMembers(self.tvNCMembers)))
  2757.         if nr == self.ntbkNewPrinterPages["downloadable"]: # Downloadable drivers
  2758.             if self.ntbkNPDownloadableDriverProperties.get_current_page() == 1:
  2759.                 accepted = self.rbtnNPDownloadLicenseYes.get_active ()
  2760.             else:
  2761.                 accepted = True
  2762.  
  2763.             self.btnNPForward.set_sensitive(accepted)
  2764.  
  2765.     # PPD
  2766.  
  2767.     def on_rbtnNPFoomatic_toggled(self):
  2768.         rbtn1 = self.rbtnNPFoomatic.isChecked()
  2769.         rbtn2 = self.rbtnNPPPD.isChecked()
  2770.         rbtn3 = self.rbtnNPDownloadableDriverSearch.isChecked()
  2771.         self.tvNPMakes.setEnabled(rbtn1)
  2772.         self.filechooserPPD.setEnabled(rbtn2)
  2773.  
  2774.         """FIXME
  2775.         if not rbtn3 and self.openprinting_query_handle:
  2776.             # Need to cancel a search in progress.
  2777.             self.openprinting.cancelOperation (self.openprinting_query_handle)
  2778.             self.openprinting_query_handle = None
  2779.             self.btnNPDownloadableDriverSearch_label.setText(_("Search"))
  2780.             # Clear printer list.
  2781.             self.cmbNPDownloadableDriverFoundPrinters.clear()
  2782.         """
  2783.  
  2784.         for widget in [self.entNPDownloadableDriverSearch,
  2785.                        self.cmbNPDownloadableDriverFoundPrinters]:
  2786.             widget.setEnabled(rbtn3)
  2787.         self.btnNPDownloadableDriverSearch.\
  2788.             setEnabled(rbtn3 and (self.openprinting_query_handle == None))
  2789.  
  2790.         self.setNPButtons()
  2791.  
  2792.     # PPD from foomatic
  2793.  
  2794.     def fillMakeList(self):
  2795.         makes = self.ppds.getMakes()
  2796.         self.tvNPMakes.clear()
  2797.         found = False
  2798.         index = 0
  2799.         for make in makes:
  2800.             self.tvNPMakes.addItem(make)
  2801.             index = index + 1
  2802.             if make==self.auto_make:
  2803.                 self.tvNPMakes.setCurrentRow(index-1)
  2804.                 found = True
  2805.  
  2806.         self.on_tvNPMakes_cursor_changed()
  2807.  
  2808.     def on_tvNPMakes_cursor_changed(self):
  2809.         items = self.tvNPMakes.selectedItems()
  2810.         if len(items) > 0:
  2811.             self.NPMake = unicode(items[0].text())
  2812.             self.fillModelList()
  2813.  
  2814.     def fillModelList(self):
  2815.         models = self.ppds.getModels(self.NPMake)
  2816.         self.tvNPModels.clear()
  2817.         selected = False
  2818.         index = 0
  2819.         selected = False
  2820.         for pmodel in models:
  2821.             self.tvNPModels.addItem(pmodel)
  2822.             if self.NPMake==self.auto_make and pmodel==self.auto_model:
  2823.                 self.tvNPModels.setCurrentRow(index)
  2824.                 selected = True
  2825.             index = index + 1
  2826.         if not selected:
  2827.             self.tvNPModels.setCurrentRow(0)
  2828.         ##self.tvNPModels.columns_autosize()
  2829.         self.on_tvNPModels_cursor_changed()
  2830.  
  2831.     def fillDriverList(self, pmake, pmodel):
  2832.         self.NPModel = pmodel
  2833.         self.tvNPDrivers.clear()
  2834.  
  2835.         ppds = self.ppds.getInfoFromModel(pmake, pmodel)
  2836.  
  2837.         self.NPDrivers = self.ppds.orderPPDNamesByPreference(ppds.keys()) 
  2838.         for i in range (len(self.NPDrivers)):
  2839.             ppd = ppds[self.NPDrivers[i]]
  2840.             driver = ppd["ppd-make-and-model"]
  2841.             driver = driver.replace(" (recommended)", "")
  2842.  
  2843.             try:
  2844.                 lpostfix = " [%s]" % ppd["ppd-natural-language"]
  2845.                 driver += lpostfix
  2846.             except KeyError:
  2847.                 pass
  2848.  
  2849.             if i == 0:
  2850.                 self.tvNPDrivers.addItem(driver + _(" (recommended)"))
  2851.                 self.tvNPDrivers.setCurrentRow(0)
  2852.             else:
  2853.                 self.tvNPDrivers.addItem(driver)
  2854.         ##self.tvNPDrivers.columns_autosize()
  2855.  
  2856.     def on_tvNPModels_cursor_changed(self):
  2857.         items = self.tvNPModels.selectedItems()
  2858.         if len(items) > 0:
  2859.             pmodel = unicode(items[0].text())
  2860.             self.fillDriverList(self.NPMake, pmodel)
  2861.  
  2862.     def getNPPPD(self):
  2863.         try:
  2864.             if self.rbtnNPFoomatic.isChecked():
  2865.                 #items = self.tvNPDrivers.selectedItems()
  2866.                 #nr = unicode(items[0])
  2867.                 nr = self.tvNPDrivers.currentRow()
  2868.                 ppd = self.NPDrivers[nr]
  2869.             elif self.rbtnNPPPD.isChecked():
  2870.                 ppd = cups.PPD(unicode(self.filechooserPPD.getText()))
  2871.             else:
  2872.                 """FIXME
  2873.                 # PPD of the driver downloaded from OpenPrinting XXX
  2874.                 treeview = self.tvNPDownloadableDrivers
  2875.                 model, iter = treeview.get_selection ().get_selected ()
  2876.                 driver = model.get_value (iter, 1)
  2877.                 if driver.has_key ('ppds'):
  2878.                     # Only need to download a PPD.
  2879.                     file_to_download = driver
  2880.                 """
  2881.  
  2882.                 ppd = "XXX"
  2883.  
  2884.         except RuntimeError, e:
  2885.             if self.rbtnNPFoomatic.isChecked():
  2886.                 # Foomatic database problem of some sort.
  2887.                 err_title = _('Database error')
  2888.                 err_text = _("The '%s' driver cannot be "
  2889.                              "used with printer '%s %s'.")
  2890.                 model, iter = (self.tvNPDrivers.get_selection().
  2891.                                get_selected())
  2892.                 nr = model.get_path(iter)[0]
  2893.                 driver = self.NPDrivers[nr]
  2894.                 if driver.startswith ("gutenprint"):
  2895.                     # This printer references some XML that is not
  2896.                     # installed by default.  Point the user at the
  2897.                     # package they need to install.
  2898.                     err = _("You will need to install the '%s' package "
  2899.                             "in order to use this driver.") % \
  2900.                             "gutenprint-foomatic"
  2901.                 else:
  2902.                     err = err_text % (driver, self.NPMake, self.NPModel)
  2903.             elif self.rbtnNPPPD.isChecked():
  2904.                 # This error came from trying to open the PPD file.
  2905.                 err_title = _('PPD error')
  2906.                 filename = self.filechooserPPD.get_filename()
  2907.                 err = _('Failed to read PPD file.  Possible reason '
  2908.                         'follows:') + '\n'
  2909.                 os.environ["PPD"] = filename
  2910.                 # We want this to be in the current natural language,
  2911.                 # so we intentionally don't set LC_ALL=C here.
  2912.                 p = os.popen ('/usr/bin/cupstestppd -rvv "$PPD"', 'r')
  2913.                 output = p.readlines ()
  2914.                 p.close ()
  2915.                 err += reduce (lambda x, y: x + y, output)
  2916.             else:
  2917.                 # Failed to get PPD downloaded from OpenPrinting XXX
  2918.                 err_title = _('Downloadable drivers')
  2919.                 err_text = _("Support for downloadable "
  2920.                              "drivers is not yet completed.")
  2921.  
  2922.             error_text = ('<span weight="bold" size="larger">' +
  2923.                           err_title + '</span>\n\n' + err)
  2924.             QMessageBox.critical(self, err_title, error_text)
  2925.             return None
  2926.  
  2927.         if isinstance(ppd, str) or isinstance(ppd, unicode):
  2928.             try:
  2929.                 if (ppd != "raw"):
  2930.                     f = self.mainapp.cups.getServerPPD(ppd)
  2931.                     ppd = cups.PPD(f)
  2932.                     os.unlink(f)
  2933.             except AttributeError:
  2934.                 nonfatalException()
  2935.                 debugprint ("pycups function getServerPPD not available: never mind")
  2936.             except RuntimeError:
  2937.                 nonfatalException()
  2938.                 debugprint ("libcups from CUPS 1.3 not available: never mind")
  2939.             except cups.IPPError:
  2940.                 nonfatalException()
  2941.                 debugprint ("CUPS 1.3 server not available: never mind")
  2942.  
  2943.         return ppd
  2944.  
  2945.     # Create new Printer
  2946.     @pyqtSignature("")
  2947.     def on_btnNPApply_clicked(self):
  2948.         if self.dialog_mode in ("class", "printer"):
  2949.             name = unicode(self.entNPName.text())
  2950.             location = unicode(self.entNPLocation.text())
  2951.             info = unicode(self.entNPDescription.text())
  2952.         else:
  2953.             name = self.mainapp.printer.name
  2954.  
  2955.         # Whether to check for missing drivers.
  2956.         check = False
  2957.         checkppd = None
  2958.         ppd = self.ppd
  2959.  
  2960.         if self.dialog_mode=="class":
  2961.             members = self.mainapp.getCurrentClassMembers(self.tvNCMembers)
  2962.             try:
  2963.                 for member in members:
  2964.                     self.passwd_retry = False # use cached Passwd 
  2965.                     self.mainapp.cups.addPrinterToClass(str(member), name)
  2966.             except cups.IPPError, (e, msg):
  2967.                 self.show_IPP_Error(e, msg)
  2968.                 return
  2969.         elif self.dialog_mode=="printer":
  2970.             self.device.uri = unicode(self.device.uri)
  2971.             uri = None
  2972.             if self.device.uri:
  2973.                 uri = self.device.uri
  2974.             else:
  2975.                 uri = self.getDeviceURI()
  2976.             if not self.ppd: # XXX needed?
  2977.                 # Go back to previous page to re-select driver.
  2978.                 self.nextNPTab(-1)
  2979.                 return
  2980.  
  2981.             # write Installable Options to ppd
  2982.             for option in self.options.itervalues():
  2983.                 option.writeback()
  2984.  
  2985.             #self.busy(self)
  2986.             self.WaitWindow.setText('<b>' +
  2987.                                      _('Adding') + '</b><br /><br />' +
  2988.                                      _('Adding printer'))
  2989.             #self.WaitWindow.set_transient_for (self.NewPrinterWindow)
  2990.             self.WaitWindow.show ()
  2991.             QApplication.processEvents()
  2992.             try:
  2993.                 self.passwd_retry = False # use cached Passwd
  2994.                 if isinstance(ppd, str) or isinstance(ppd, unicode):
  2995.                     self.mainapp.cups.addPrinter(name, ppdname=ppd,
  2996.                          device=uri, info=info, location=location)
  2997.                     check = True
  2998.                 elif ppd is None: # raw queue
  2999.                     self.mainapp.cups.addPrinter(name, device=uri,
  3000.                                          info=info, location=location)
  3001.                 else:
  3002.                     cupshelpers.setPPDPageSize(ppd, self.language[0])
  3003.                     self.mainapp.cups.addPrinter(name, ppd=ppd,
  3004.                          device=uri, info=info, location=location)
  3005.                     check = True
  3006.                     checkppd = ppd
  3007.                 cupshelpers.activateNewPrinter (self.mainapp.cups, name)
  3008.             except cups.IPPError, (e, msg):
  3009.                 #self.ready(self)
  3010.                 self.WaitWindow.hide ()
  3011.                 self.show_IPP_Error(e, msg)
  3012.                 return
  3013.             except:
  3014.                 ##self.ready (self.NewPrinterWindow)
  3015.                 self.WaitWindow.hide ()
  3016.                 fatalException (1)
  3017.             self.WaitWindow.hide ()
  3018.             ##self.ready (self.NewPrinterWindow)
  3019.     #comment  
  3020.         if self.dialog_mode in ("class", "printer"):
  3021.             try:
  3022.                 self.passwd_retry = False # use cached Passwd 
  3023.                 self.mainapp.cups.setPrinterLocation(name, location)
  3024.                 self.passwd_retry = False # use cached Passwd 
  3025.                 self.mainapp.cups.setPrinterInfo(name, info)
  3026.             except cups.IPPError, (e, msg):
  3027.                 self.show_IPP_Error(e, msg)
  3028.                 return
  3029.         elif self.dialog_mode == "device":
  3030.             try:
  3031.                 uri = self.getDeviceURI()
  3032.                 self.mainapp.cups.addPrinter(name, device=uri)
  3033.             except cups.IPPError, (e, msg):
  3034.                 self.show_IPP_Error(e, msg)
  3035.                 return 
  3036.         elif self.dialog_mode == "ppd":
  3037.             if not ppd:
  3038.                 ppd = self.ppd = self.getNPPPD()
  3039.                 if not ppd:
  3040.                     # Go back to previous page to re-select driver.
  3041.                     self.nextNPTab(-1)
  3042.                     return
  3043.  
  3044.             # set ppd on server and retrieve it
  3045.             # cups doesn't offer a way to just download a ppd ;(=
  3046.             raw = False
  3047.             if isinstance(ppd, str) or isinstance(ppd, unicode):
  3048.                 if self.rbtnChangePPDasIs.isChecked():
  3049.                     # To use the PPD as-is we need to prevent CUPS copying
  3050.                     # the old options over.  Do this by setting it to a
  3051.                     # raw queue (no PPD) first.
  3052.                     try:
  3053.                         self.mainapp.cups.addPrinter(name, ppdname='raw')
  3054.                     except cups.IPPError, (e, msg):
  3055.                         self.show_IPP_Error(e, msg)
  3056.                 try:
  3057.                     self.mainapp.cups.addPrinter(name, ppdname=ppd)
  3058.                 except cups.IPPError, (e, msg):
  3059.                     self.show_IPP_Error(e, msg)
  3060.                     return
  3061.  
  3062.                 try:
  3063.                     filename = self.mainapp.cups.getPPD(name)
  3064.                     ppd = cups.PPD(filename)
  3065.                     os.unlink(filename)
  3066.                 except cups.IPPError, (e, msg):
  3067.                     if e == cups.IPP_NOT_FOUND:
  3068.                         raw = True
  3069.                     else:
  3070.                         self.show_IPP_Error(e, msg)
  3071.                         return
  3072.             else:
  3073.                 # We have an actual PPD to upload, not just a name.
  3074.                 if not self.rbtnChangePPDasIs.isChecked():
  3075.                     cupshelpers.copyPPDOptions(self.mainapp.ppd, ppd) # XXX
  3076.                 else:
  3077.                     # write Installable Options to ppd
  3078.                     for option in self.options.itervalues():
  3079.                         option.writeback()
  3080.                     cupshelpers.setPPDPageSize(ppd, self.language[0])
  3081.  
  3082.                 try:
  3083.                     self.mainapp.cups.addPrinter(name, ppd=ppd)
  3084.                 except cups.IPPError, (e, msg):
  3085.                     self.show_IPP_Error(e, msg)
  3086.  
  3087.             if not raw:
  3088.                 check = True
  3089.                 checkppd = ppd
  3090.  
  3091.         self.accept()
  3092.         self.mainapp.populateList(start_printer=name)
  3093.         if check:
  3094.             try:
  3095.                 self.checkDriverExists (name, ppd=checkppd)
  3096.             except:
  3097.                 nonfatalException()
  3098.  
  3099.             # Also check to see whether the media option has become
  3100.             # invalid.  This can happen if it had previously been
  3101.             # explicitly set to a page size that is not offered with
  3102.             # the new PPD (see bug #441836).
  3103.             """
  3104.             try:
  3105.                 option = self.mainapp.server_side_options['media']
  3106.                 if option.get_current_value () == None:
  3107.                     debugprint ("Invalid media option: resetting")
  3108.                     option.reset ()
  3109.                     self.mainapp.changed.add (option)
  3110.                     self.mainapp.save_printer (self.mainapp.printer)
  3111.             except KeyError:
  3112.                 pass
  3113.             except:
  3114.                 print "exception in check to see whether the media option has become invalid"
  3115.                 nonfatalException()
  3116.             """
  3117.  
  3118.     def checkDriverExists(self, name, ppd=None):
  3119.         """Check that the driver for an existing queue actually
  3120.         exists, and prompt to install the appropriate package
  3121.         if not.
  3122.  
  3123.         ppd: cups.PPD object, if already created"""
  3124.  
  3125.         # Is this queue on the local machine?  If not, we can't check
  3126.         # anything at all.
  3127.         server = cups.getServer ()
  3128.         if not (server == 'localhost' or server == '127.0.0.1' or
  3129.                 server == '::1' or server[0] == '/'):
  3130.             return
  3131.  
  3132.         # Fetch the PPD if we haven't already.
  3133.         if not ppd:
  3134.             try:
  3135.                 filename = self.mainapp.cups.getPPD(name)
  3136.             except cups.IPPError, (e, msg):
  3137.                 if e == cups.IPP_NOT_FOUND:
  3138.                     # This is a raw queue.  Nothing to check.
  3139.                     return
  3140.                 else:
  3141.                     self.show_IPP_Error(e, msg)
  3142.                     return
  3143.  
  3144.             ppd = cups.PPD(filename)
  3145.             os.unlink(filename)
  3146.  
  3147.         (pkgs, exes) = cupshelpers.missingPackagesAndExecutables (ppd)
  3148.         if len (pkgs) > 0 or len (exes) > 0:
  3149.             # We didn't find a necessary executable.  Complain.
  3150.             install = "/usr/bin/system-install-packages"
  3151.             if len (pkgs) > 0 and os.access (install, os.X_OK):
  3152.                 pkg = pkgs[0]
  3153.                 install_text = ('<span weight="bold" size="larger">' +
  3154.                                 _('Install driver') + '</span>\n\n' +
  3155.                                 _("Printer '%s' requires the %s package but "
  3156.                                   "it is not currently installed.") %
  3157.                                 (name, pkg))
  3158.                 dialog = self.InstallDialog
  3159.                 self.lblInstall.set_markup(install_text)
  3160.             else:
  3161.                 error_text = ('<span weight="bold" size="larger">' +
  3162.                               _('Missing driver') + '</span>\n\n' +
  3163.                               _("Printer '%s' requires the '%s' program but "
  3164.                                 "it is not currently installed.  Please "
  3165.                                 "install it before using this printer.") %
  3166.                               (name, (exes + pkgs)[0]))
  3167.                 QMessageBox.error(self, "", error_text)
  3168.  
  3169.             """
  3170.             if pkg and response == gtk.RESPONSE_OK:
  3171.                 # Install the package.
  3172.                 def wait_child (sig, stack):
  3173.                     (pid, status) = os.wait ()
  3174.  
  3175.                 signal.signal (signal.SIGCHLD, wait_child)
  3176.                 pid = os.fork ()
  3177.                 if pid == 0:
  3178.                     # Child.
  3179.                     try:
  3180.                         os.execv (install, [install, pkg])
  3181.                     except:
  3182.                         pass
  3183.                     sys.exit (1)
  3184.                 elif pid == -1:
  3185.                     pass # should handle error
  3186.             """
  3187.  
  3188.     #FIXME obsolete?
  3189.     def on_entNPTIPPQueuename_textChanged(self, ent):
  3190.         self.update_IPP_URI_label ()
  3191.  
  3192.     #FIXME obsolete?
  3193.     def on_IPPBrowseBox_currentTextChanged(self, text):
  3194.         self.update_IPP_URI_label()
  3195.  
  3196.     #FIXME not in gnome?
  3197.     @pyqtSignature("")
  3198.     def on_btnNPCancel_clicked(self):
  3199.         self.hide()
  3200. #end of class NewPrinterGUI
  3201.  
  3202. if __name__ == "__main__":
  3203.     """start the application.  TODO, gtk frontend does clever things here to not start the GUI until it has to"""
  3204.     app = QApplication(sys.argv)
  3205.     applet = GUI()
  3206.     sys.exit(app.exec_())
  3207.